結論先講

三場測試、三個結論:Queue 看生態不看速度。Redis cache 值得開。Cold Start 選 Go。 不像 CRUD 和混合場景有戲劇性的排名翻盤,這三場測試的結論比較務實——不是「誰最好」,而是「什麼時候用什麼」。


Queue:同步 vs 非同步佇列

測試場景

項目設定
流程建立 Notification → dispatch 到 Queue → Worker 處理
比較同步(直接處理)vs 非同步(丟 Queue 再處理)
框架9 框架,各自使用生態內的 Queue 方案

各框架的 Queue 方案

框架Queue 方案特性
Express-TS / NestJSBullMQ(Redis-backed)成熟、支援 retry/delay/priority
Express-JSBull(BullMQ 前身)穩定但功能較少
DjangoCelery(Redis/RabbitMQ)Python 生態標準
FastAPICeleryarqasyncio 友好
Gogoroutine + channelasynq不需要外部依賴
Spring BootSpring Async + thread poolJVM 原生支援
LaravelLaravel Queue(Redis/DB)框架內建
.NET CoreHangfireMassTransit企業級

結論:差異在生態不在速度

所有框架都能處理同步和非同步佇列任務。非同步化的好處不是「更快」,而是「讓 response time 更短」——API 立刻回 202 Accepted,實際工作在背景完成。

同步: Client → API → 處理任務(500ms) → 回 200
非同步: Client → API → 丟 Queue(5ms) → 回 202 → Worker 背景處理(500ms)

對用戶來說,API response time 從 500ms 降到 5ms。 但任務完成時間沒變。

Queue 的真正用途

Queue 不是用來「加速」的,是用來:

  1. 削峰: 大促期間 1000 個訂單同時進來,API 立刻回 202,Queue 慢慢處理
  2. 解耦: 下訂單和發 email 分開,email 服務掛了不影響訂單
  3. 重試: Worker 處理失敗可以自動重試,不需要用戶重新操作
  4. 延遲任務: 30 分鐘後自動取消未付款訂單

選 Queue 的建議

  • 已經有 Redis → BullMQ(Node.js)或 Laravel Queue(PHP)
  • 需要複雜路由 → RabbitMQ
  • 需要事件重播 → Kafka(回顧 Storage 篇
  • Go → 簡單場景用 goroutine + channel,複雜場景用 asynq(Redis-backed)

Redis Cache On/Off:開了就是比較快

測試場景

項目設定
流程同一個 User CRUD workload
比較CACHE_ENABLED=true vs CACHE_ENABLED=false
框架9 框架

結果

開啟 Redis cache 後,所有框架的讀取效能都有明顯提升。差異跟各框架的 Redis client 效率有關。

這和 Infra 免費午餐篇 的 cache-aside 數據一致:

場景無 Cache有 Redis Cache提升
讀多(80:20)2,723 req/s17,833 req/s6.5 倍
標準 CRUD(50:50)2,614 req/s3,266 req/s25%

但對 bcrypt 場景效果有限

B2E 的 Redis On/Off 測試是在 Auth + CRUD workload 上做的(包含 bcrypt)。Redis cache 幫助的是讀取操作(跳過 DB 查詢),但幫不了 bcrypt——密碼 hash 是 CPU 計算,不是 DB 查詢。

回顧 Bcrypt 篇 的結論:Redis cache 對 auth 場景的影響 < 5%,但對讀取場景關鍵。 你的應用讀寫比越高,Redis 的效果越大。

各框架的 Redis Client

框架Redis Client備註
Express-TS / NestJSioredis最成熟,支援 cluster
Djangodjango-redis + redis-py需要額外裝
FastAPIaioredisredis-py[async]async 支援
Gogo-redis原生支援
Spring Bootspring-data-redis + Lettuce框架內建
Laravelpredisphpredis框架內建
.NET CoreStackExchange.Redis成熟

Cold Start:Go 最快、NestJS 最慢

測試場景

項目設定
定義docker compose up 到第一個 health check 回 200 的時間
包含container 啟動 + runtime 初始化 + DB 連線 + 第一個請求處理

結果

框架Cold Start備註
Go6.7 秒靜態編譯 binary,啟動即運行
.NET Core6.7 秒CLR 預編譯 (ReadyToRun)
Express-JS~10 秒Node.js 啟動 + require()
Express-TS~12 秒同上,但多 TypeScript 編譯快取
Django~12 秒Python import + migration check
FastAPI~12 秒同 Django
Laravel~15 秒PHP-FPM + OPcache warm-up
Spring Boot~20 秒JVM 啟動 + Spring context 初始化
NestJS30.3 秒PM2 啟動 4 個 workers,每個都要初始化

為什麼 NestJS 最慢

NestJS 本身的啟動不慢(單 instance 約 8 秒),但我們的配置用 PM2 cluster mode 啟動 4 個 workers。4 個 Node.js process 各自:

  1. 載入 NestJS 框架
  2. 掃描所有 Module 和 Provider
  3. 初始化 DI container
  4. 建立 DB 連線
  5. 回報 ready

4 個 process × 每個 ~8 秒 ≈ 30 秒(不是並行的,因為 CPU 被搶)。

為什麼 Go 和 .NET Core 一樣快

兩者都是「編譯成 native code」的思路:

  • Go: 靜態編譯成單一 binary,沒有 runtime 啟動成本
  • .NET Core: ReadyToRun (R2R) 預編譯,跳過大部分 JIT

Spring Boot 慢是因為 JVM 啟動 + Spring 的 annotation scanning + bean 初始化。這是 Java 生態的老問題——GraalVM Native Image 可以改善但目前相容性不夠好。

對 Serverless 的意義

部署模式Cold Start 影響
傳統伺服器(長時間運行)無影響——啟動一次跑幾個月
容器化(K8s auto-scaling)中等——新 Pod 啟動時用戶會等
Serverless(每次請求可能 cold start)關鍵——每個 cold start 都是用戶體驗損失

如果你的目標是 Serverless / Edge Function:

  • 選 Go:6.7 秒 container 啟動,但 Serverless 場景的 binary cold start < 100ms
  • 避免 Spring Boot:20 秒 container 啟動,JVM cold start 幾秒
  • 避免 NestJS PM2 mode:30 秒太長。如果非要用 NestJS,改單 instance 模式

三場測試的共同 takeaway

測試結論行動項目
Queue差異在生態不在速度已有 Redis 就用 BullMQ/Laravel Queue
Redis Cache開了都比較快讀多場景一定要開
Cold StartGo 最快,NestJS 最慢Serverless 選 Go

這三場不像 CRUD 和混合場景那樣有戲劇性排名翻盤。它們的價值在補完選型地圖——回顧 容量規劃表,現在每一格的建議都有數據支撐了。


系列接下來

B2E 的 10 項測試全部覆蓋完畢。接下來可以寫的:

  • 微服務經驗:從單體到微服務,壓測突然變重要的故事
  • 系列回顧:壓測教會我們的事

下一篇

從單體到微服務:UV 10 就報錯,才知道要拆 — B2E 的 10 項測試全部跑完。接下來講我們自己的故事——從單體拆成微服務踩了哪些坑、壓測平台怎麼誕生的。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:WebSocket 與 SSE 壓測:所有框架都能撐,差異不在這裡 → 下一篇:從單體到微服務:UV 10 就報錯,才知道要拆