全端開發2024

Discord 風格聊天應用

模仿 Discord 架構的即時聊天應用,支援伺服器、頻道、私訊與好友系統,前後端採 Vue 3 + Go 開發,後端現已支援 K3s 水平擴展與 Redis Pub/Sub 跨 Pod 廣播。

Discord 風格聊天應用

專案概述

以 Discord 為樣板製作的即時聊天應用,包含前端(Vue 3)和後端(Go)兩個部分。支援伺服器(Guild)、頻道(Channel)、私訊(DM)、好友系統、檔案上傳與 WebSocket 即時訊息。專案初版以單實例 WebSocket Hub 為核心,後續逐步演進為可部署於 K3s 的即時系統:透過 Redis Pub/Sub 將 room 事件跨 Pod 傳播,讓多個 chat-app 實例在水平擴展後仍能維持一致的即時推播體驗;同時搭配 MongoDB、Redis、Prometheus、pprof、k6 與 ArgoCD/GitOps,將聊天室從功能實作推進到接近生產環境的部署與驗證模式。

技術挑戰與解決方案

從單實例 WebSocket 演進到跨 Pod 廣播

原本的聊天室即時層以單程序記憶體內的 Room / Client 結構為核心,當服務部署到多個 Pod 時,訊息只會送到連線所在的單一實例,無法跨 Pod 廣播。

解決方案:
保留本地 RoomManager 模型,但在訊息寫入 MongoDB 後,透過 Redis Pub/Sub 對 room:: 發布事件。每個 Pod 在動態初始化房間時訂閱對應 channel,收到訊息後再轉發給本機連線,讓 K3s 水平擴展後仍能維持一致的即時聊天體驗。

JWT 驗證與安全性

需要為 SPA 實作安全的無狀態認證系統,同時在多 Pod 部署下仍維持登入、刷新與撤銷流程一致。

解決方案:
採用 JWT Access/Refresh Token 滾動策略。Refresh Token 存放於 httpOnly Cookie(防 XSS),並同步持久化至 MongoDB,確保任一實例都能驗證與撤銷;再搭配 CSRF Middleware 提供額外防護。

K3s 部署與水平擴展驗證

Docker Compose 可以跑起來,但無法驗證真實的多副本負載均衡、Probe、滾動更新與資源擴縮行為。

解決方案:
補上 K8s manifest、Kustomize overlays 與 ArgoCD Application,讓系統能在 K3s 中以 Deployment + Service 運行,並透過 HPA 依 CPU / Memory 自動在 2 到 10 個 Pod 間擴縮;本地也可用 make k8s-deploy / make k8s-scale 反覆驗證。

檔案儲存與無狀態 Pod 限制

聊天應用需要頭像、伺服器圖片等檔案上傳,但 K3s Pod 是無狀態的,本地 uploads 目錄在重建或擴容後可能遺失或不一致。

解決方案:
將檔案層抽象為 provider,開發環境維持本地 uploads,加快迭代;同時保留 MinIO / 物件儲存的切換能力,讓架構能在需要時平滑改接外部儲存服務,而不是把檔案邏輯綁死在單一 Pod 的本地磁碟。

可觀測性與擴展行為驗證

水平擴展後如果沒有指標與壓測資料,很難判斷瓶頸究竟在 WebSocket、Redis、MongoDB 還是 Pod 資源配額。

解決方案:
整合 go-gin-prometheus 暴露 /metrics,並補上 pprof、Grafana/Prometheus 監控設計與 k6 壓測腳本,分別驗證單機、Docker Compose 多實例與 K8s 水平擴展情境,讓效能調校與 HPA 行為有具體數據可依據。

系統架構

前端:Vue 3 + TypeScript + Pinia + Element Plus。後端:Go 1.25 + Gin + gorilla/websocket,維持 Controller → Service → Repository 三層架構與手寫依賴注入。即時層以 RoomManager 管理房間與本機 Client,MessageHandler 在訊息入庫後透過 Redis Publish 發送到 room:<type>:<id> channel,各 Pod 在 InitRoom 時訂閱對應 channel,收到事件後只轉發給本機 WebSocket 連線,解決水平擴展後的跨實例廣播問題。部署層使用 K3s、Deployment、HPA、Kustomize 與 ArgoCD 管理多 Pod 環境,並以 Prometheus、pprof 和 k6 觀察效能與擴展行為。

學習與心得

這個專案讓我從單機即時聊天應用,往真正可擴展的即時系統多走了一步。我不只學會 Go 後端、JWT 與 WebSocket 連線管理,也實際面對了水平擴展後最麻煩的狀態同步問題:當 WebSocket 連線分散到多個 Pod,訊息就不能只靠記憶體廣播。我透過 Redis Pub/Sub、K3s/HPA 與壓測驗證,理解了即時系統在多實例環境中的資料流、無狀態部署限制,以及部署、監控與負載測試必須一起設計。

技術棧

前端

Vue 3TypeScriptElement PlusPiniaUnoCSS

後端

Go 1.25Gingorilla/websocketJWTRedis Pub/Sub

資料庫

MongoDBRedis

容器調度

K3sHPAKustomizeArgoCD

監控與可觀測性

Prometheuspprofk6