RedLock 分散式鎖在 CacheLayerManager 的實作案例

背景問題:Cache Stampede(快取雪崩)

在高並發環境下,當某個 Redis cache key 同時過期,大量請求會同時發現 cache miss 並全部打向後端 DB / Provider,這就是 Cache Stampede

flowchart TD
    E[Cache Key 過期] --> A[Request A Cache Miss]
    E --> B[Request B Cache Miss]
    E --> C[Request C Cache Miss]
    E --> D[Request D Cache Miss]
    A --> P[Call Provider]
    B --> P
    C --> P
    D --> P
    P --> DB[DB 或 Provider 被打爆]

解決方案: 在更新 Cache 時加 分散式鎖(RedLock),確保同一時間只有一個 instance 去 Provider 取資料並回填 Cache。

Read More

WatcherUtil — 用 Delegate 實現 AOP 效能監控,不污染業務邏輯

問題:效能監控程式碼到處散落

一般做法是在每個方法手動埋計時:

1
2
3
4
5
6
7
8
9
// 到處複製貼上,業務邏輯被監控代碼淹沒
var start = DateTime.Now;
try {
result = db.GetSomething(key);
var ms = (DateTime.Now - start).TotalMilliseconds;
log.Performance("REDIS", "GetSomething", ms.ToString(), "");
} catch (Exception ex) {
log.Exception(ex);
}

這違反了 單一職責原則(SRP),監控邏輯和業務邏輯耦合在一起。

Read More

雙層快取架構:LazyCache (L1) + Redis (L2) 防止 Cache Stampede

為什麼需要雙層快取?

使用場景

典型情境是 多台 App Server 水平擴展,讀取路徑上會先查本機記憶體、再查 共用的 Redis(L2) 當成跨機器的快取來源。業務資料可能在後端更新後寫回 Redis,或依 TTL 在 Redis 上過期;各機的 L1 也會依自己的 TTL 失效。只要出現「同一時間很多請求都認為快取不可用」——例如熱點 key 的 TTL 一起到期、部署後快取清空、或 Redis 短暫不可用後恢復——每一台機、每一條執行緒若都各自回源,後端與 Redis 會在瞬間承受 N 台 × 每機併發 的壓力。

這套 L1(LazyCache)+ L2(Redis) 的組合,目的就是在這種 多機共享快取、又需要週期或事件驅動更新 的架構下,把「miss 之後誰負責載入」收斂成 單機內合併請求,必要時再在 L2 層用分散式鎖(例如 RedLock,見 CacheLayerManager)避免多機同時把同一個 key 打穿到資料庫。它不是取代「資料在業務上如何同步」的設計,而是降低 快取重建瞬間 的並發災難。

Read More

Debug Mode 下動態抓取 manifest.json — _Layout.cshtml 的 JS 版本管理機制

背景

正式環境下,前端 JS/CSS 經 Webpack 打包後會帶 content hash 檔名;ASP.NET MVC 的 _Layout.cshtml 必須依 manifest.json 注入正確的 <script> / <link>。本篇說明在 Debug 時如何 不必重啟後端 也能對齊 Webpack 即時編譯 的輸出。

專案型態:前後端未分離的 .NET Framework MVC

我們的專案 不是「獨立前端站 + 獨立 API 站」那種典型前後端分離:

  • 後端.NET Framework 上的 ASP.NET MVC。頁面骨架仍由 Razor(例如 _Layout.cshtml)在伺服器端渲染,路由與部分畫面由 MVC 負責。
  • 前端互動:實際的 SPA/模組化 UI 由 Webpack 打包的 JS、CSS 在瀏覽器端啟動;正式環境這些靜態檔會放到 CDN,透過 hash 檔名 做快取破壞。
  • Debug 開發:本機不強制走「部署到 CDN + 重讀 manifest」那條路,而是啟動 Webpack Dev Server(或同等 dev 流程),由它 動態編譯並提供 最新的 vendor.jsindex.js 等。_LayoutDebug 組態下改把資源 URL 指到 本機 dev server,因此 重新 build / HMR 後只要重新整理頁面,即可載入新 bundle,無須為了更新 hash 而去重啟 IIS 或整個 .NET 站台

簡單講:Release=後端讀記憶體裡的 manifest.json,對應 CDN 上帶 hash 的檔;Debug=後端略過 manifest,改連 dev server 上「當下編譯出來」的固定路徑(無 hash 或 dev 產物),讓「改前端 → 立刻看到畫面」。

Read More

責任鏈模式(Chain of Responsibility)泛型實作 — 遊戲驗證與頁面權限的真實案例

什麼是責任鏈模式?

當一個請求需要經過多個獨立的檢查/處理步驟,且每個步驟都可以決定「繼續往下傳」或「在這裡中止」,就是責任鏈(Chain of Responsibility)的使用場景。

實務上:一連串檢查,拆開再組合

在後端我們很常把 同一條業務路徑 上的條件拆成 多個彼此獨立的檢查,而不是寫成一大段 if/else 攪在一起。例如提款或資金相關流程可能依序要確認:

  • 帳號狀態(凍結、風控、KYC 等)
  • 存款/餘額門檻(是否達最低可提、餘額是否足夠)
  • 是否已有進行中的提款設定(避免重複提交、與進行中單據衝突)

每一項都是 不同的業務規則、不同的失敗原因與錯誤訊息。用責任鏈時,一個檢查一個 Handler:職責單一、單元測試好寫、命名也清楚。更重要的是,同一個 Handler 可以在別的流程再組一條鏈——例如「帳號狀態檢查」同時出現在提款、轉帳、兌換;順序與要不要掛某一關,只在組裝鏈的地方決定,不必複製貼上整段邏輯。

下列為 示意(實際類別名與順序依專案而定),重點是 每一關可獨立抽換、也可在別的情境重用

flowchart TD
    W[資金流程請求示意] --> ACC[帳號狀態檢查]
    ACC -->|pass| BAL[存款餘額或門檻檢查]
    BAL -->|pass| PEND[進行中提款設定檢查]
    PEND -->|pass| DONE[後續業務邏輯]
    ACC -->|fail| X1[中止並回傳原因]
    BAL -->|fail| X2[中止並回傳原因]
    PEND -->|fail| X3[中止並回傳原因]

Read More

Login MFA — 新裝置驗證架構設計

背景

傳統登入只驗證帳號 + 密碼,一旦帳密外洩即全面失守。本需求在既有登入流程中加入第二層驗證(MFA):在使用者完成密碼驗證後、正式取得 Passport 之前,須通過以下其中一種驗證:

方法 說明
Push Notification 向已登入的可信裝置推播,由舊手機批准
Email OTP 發送一次性驗證碼到註冊信箱
TOTP 使用 Authenticator App(時間型)驗證碼

驗證優先級:Push Notification > Email OTP > TOTP(availableMethods[0] 為預設)

核心設計決策:Passport 在驗證成功後才產生

flowchart LR
    subgraph Legacy["傳統做法 有安全風險"]
        L1[Login API] --> L2[驗證帳密]
        L2 --> L3[立即回傳 Passport]
        L3 --> L4[前端再做 MFA]
    end
    subgraph CC["CC1-4638 做法"]
        C1[Login API] --> C2[驗證帳密]
        C2 --> C3["returnCode 0018 不含 Passport"]
        C3 --> C4[MFA 驗證通過]
        C4 --> C5[產生 Passport]
        C5 --> C6[PostLogin]
    end

這確保未完成 MFA 的使用者無法取得任何有效 Passport。

Read More

自建簡易 CMS 系統架構 — 用 ASP.NET + MongoDB 打造多語系動態內容管理

前言

在多品牌、多區域、多語系的平台中,首頁的 Banner、Widget、行銷活動區塊都需要能夠由運營人員自行管理,而不是每次改內容都要動到程式碼。市面上有很多成熟的 CMS(WordPress、Strapi 等),但我們的需求比較特殊:

  • 內容需要**依區域(Region)和語系(Language)**動態切換
  • 版位採用 CSS Grid(grid-cols-12) 設計,運營人員可以自行調整 RWD 排版
  • 支援定時排程,廣告可以設定上下架時間、週期性顯示
  • 最終輸出是 HTML 片段,由前端 Vue.js 應用動態 compile 並渲染

因此我們自建了一套簡易 CMS 系統(代號 CMS),本文完整介紹這套系統的架構設計。

Read More

TheDialogQueue — 用 Pinia Store 實作全站彈窗佇列機制

前言

在平台系統中,操作過程經常同時觸發多個彈窗——HTTP 錯誤、銀行維護提示、KYC 驗證結果、交易確認等。如果各元件各自管理 v-model,多個 overlay 會同時疊加在畫面上,不僅使用者體驗混亂,對開發者來說也極度難以維護。

我們需要一個統一的 Dialog 管理機制:所有需要彈窗的地方都不自己開 Dialog,而是把「要顯示什麼」丟進佇列,由一個元件依序一個一個渲染。

Read More

Vue.js BroadcastChannel 跨分頁通訊:從踩坑到 Singleton Event Bus 架構設計

前言

在多分頁的 Web 應用中,跨 Tab 同步狀態是常見需求——例如閒置登出同步、Session 過期通知、T&C 接受後其他 Tab 強制刷新等。瀏覽器原生的 BroadcastChannel API 是最直覺的方案,搭配 VueUse 的 useBroadcastChannel 封裝更是方便。

但在實際開發中,我們踩了一個非常隱晦的坑——同一個 Tab 內建立多個 BroadcastChannel 實例,會互相收到訊息。本文記錄我們從「能用」到「好用」的架構演進過程。

Read More

跨框架 Partial View 架構設計 — 用 Vue 3 打造可嵌入任何頁面的 Header/Footer

前言

在多產品平台中,每個產品(體育、娛樂城、棋牌等)可能由不同的合作夥伴(Provider)開發,使用的技術棧也各不相同——React、Angular、甚至純 JS。但平台需要在所有產品頁面上維持一致的品牌形象、導航列、會員狀態和法律聲明。

要求每個 Provider 自行實作這些 UI 元件既不現實也難以維護。因此我們設計了 star4partialview — 一個獨立打包的 Vue 3 應用,輸出單一 partialView.js,Provider 只需引入一個 <script> 標籤就能獲得完整的 Header + Footer。

兩個獨立的 JS Runtime 透過 window.postMessage 通訊,實現了一種輕量級的 Micro Frontend 模式。

Read More