前言

前陣子我的藥師朋友經常需要手動計數藥丸,以便進行包藥作業.這個過程既耗時又容易出錯。為了解決這一問題,我幫他設計了一個簡單的應用程式,使用 YOLOv8 模型來自動識別並計算照片中藥丸的數量,從而快速提供準確的結果

專案說明

Demo

專案的詳細介紹我把他整理到 Notion 上,裏面有更詳細的說明

Ref: GitHub Repo

系統架構

這個專案需求非常簡單,其實單純一個 Monolithic 的架構就可以應付,但為了練習看看微服務(Micro service)的切分,就嘗試將不同的功能切分成不同的服務,彼此用 Restful API 溝通

architecture

Tech Stack

簡單列一下使用到的技術

Backend

  • Python(FastAPI)
  • Node.js(Express) written in TypeScript

Auth

  • JWT
  • Google Auth
  • Redis

AI Model

  • YOLOv8
  • Label-Studio (Labeling Tool)

Frontend

  • React written in TypeScript
  • PWA

Storage

  • PostgreSQL (database)
  • Minio (Image storage)

心得

  • 這是第一次嘗試 React 的 PWA,用 service worker 滿直覺也滿簡單的,配置一下部署到 Netlify 上,就可以把網站下載到手機桌面上充當一個 APP 使用

  • 這是第一次嘗試 Yolov8 的模型,並使用 Label-Studio 工具作為標註,使用起來相當直覺,當有多人需要同時標註時,簡單在電腦啟動 server,然後用 ngrok 臨時建個 tunnel,開放給別人一起標註

  • 這是第一次使用 Python 的 alembic 函式庫作為 SQLAlchemy 的資料庫版本管理工具

    • alembic 提供 upgradedowngrade 的函式來撰寫每一版本的資料庫更新腳本,可隨時針對資料庫的 schema 做升版或降版
    • 之前在開發另個電商專案時有類似的體驗,使用 Node.js 生態系的 TypeORM,一樣透過升降版的腳本來保留資料庫的歷史紀錄,可以隨時執行 updown 腳本修改資料庫的 schema,所以這次使用 alembic 除了語法差異以外,概念上不會太陌生
  • 這是第一次使用 MinIO 這個 Object Storage,概念上有點類似 AWS 的 S3,適合儲存圖片類的資源

    • 部署過程有遇到 web 在 Docker network 內沒辦法連接到 minIO 中儲存的圖片,原因在 hostname 的解析,寫個腳本執行即可

      #!/bin/sh
      
      # @Example: `sh first-start.sh`
      
      source ../.env
      docker exec -it pillCounter-minio sh -c "
          mc config host add local http://minio:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD --api S3v4 --lookup auto
          mc anonymous set public local/$MINIO_BUCKET_NAME
      "
      

      這時候連接 http://localhost:9001/pill-counter-images/xxx.png 就可以在網頁上成功顯示圖片

  • Auth Service 擔任 Reverse Proxy 和 Authenticate 的 API Gateway

    • 使用 JWT 和 Redis 實作認證,我將 session 存在 Redis 中,這樣可以減少頻繁的資料庫查詢,從而提高響應速度和整體性能

    • JWT 的 payload 記錄一些不敏感的資訊,例如 user_idusernameavatar_uri,這樣可以隨時解析 JWT 就可以獲取 user_id,不用再向資料庫查詢一次,多增加一次 round trip 的成本

    • Reverse proxy 不使用 Nginx,純粹是希望在這層 API Gateway 上有更多彈性可以滿足客製化的需求

  • 使用 Docker-compose 作為容器化部署

    • 單一 node 情況下,簡單使用 Docker compose,而沒有使用 Kubernetes
    • 每個服務都有一個 /health 的 API endpoint,可在部署後作為 health check 的依據
  • Payment Service 串接藍新金流

    • 前陣子有串接過綠界金流,對於金流的串接流程大致理解,這次改嘗試串接藍新金流,主要是因為訂閱制的需求
    • 整體串接起來的過程比綠界金流好很多,但與串接國外的 Stripe 體感上還是落差一大截

結論

這次藉由一個簡單的專案需求,嘗試了幾種技術的組合,也在過程中踩到不同的坑,實作中也學到不少東西,算是一次滿好的經驗,當然使用微服務就有更多面向需要考量,以這個專案來說確實有點過度複雜化.