跳轉到

4.1 V8 Isolate 與冷啟動歸零的原理#

本節目標#

  • 理解 Cloudflare Workers 的誕生歷史,以及它與 AWS Lambda 在設計哲學上的根本差異
  • 認識 V8 Isolate:同一個作業系統進程如何安全地容納成千上萬的租戶程式碼
  • 建立 container、VM、Isolate 三種執行模型的對比心智模型
  • 說明 Workers 的 CPU 時限規則,以及「冷啟動歸零」在數學上為何成立

歷史:2017 年,邊緣運算的宣言#

2017 年 9 月,Cloudflare 在 Birthday Week 發表 Workers。當時的敘事很簡單:CDN 的 PoP(Point of Presence)節點早就部署在世界各地的機房, 既然程式碼可以直接在使用者附近的節點執行,為什麼還要把請求送到遠方的資料中心?

傳統 Lambda 架構的路徑延遲可以估計為:

\[ T_{\text{lambda}} = T_{\text{net}}(d_{\text{far}}) + T_{\text{cold}} + t_{\text{exec}} \]

而 Workers 的目標是:

\[ T_{\text{worker}} = T_{\text{net}}(d_{\text{near}}) + \varepsilon_{\text{cold}} + t_{\text{exec}}, \quad \varepsilon_{\text{cold}} \to 0 \]

其中 \(d_{\text{near}} \ll d_{\text{far}}\) (數十公里 vs 數百至數千公里),而 \(\varepsilon_{\text{cold}}\) 是 Workers 特有的秘密,本節的重點。

Lambda 的冷啟動從哪裡來#

AWS Lambda(2014)以 Firecracker microVM 為執行單位,冷啟動流程是:

事件到達 → 找閒置 VM(或)啟動 microVM
        → 載入 runtime(Node/Python…)
        → 載入使用者程式碼與依賴
        → 執行 init hook

每一級都對應數十到數百毫秒。Node.js 專案若依賴龐大(解壓後數百 MB),冷啟動常見 100ms~數秒。對 A/B 測試、API 閘道這類毫秒敏感的場景,這是致命傷。

資源模型的形式化#

設一個請求的總延遲由網路、啟動、執行三部分組成:

\[ L = \alpha \cdot d + \beta \cdot s + \gamma \cdot e \]
  • \(d\) :使用者到資料中心的物理距離
  • \(s\) :執行環境的建立成本(冷啟動)
  • \(e\) :程式碼本身的執行時間

Lambda 的 \(\beta\) 很大(VM 級),Cloudflare 的策略是同時把 \(\alpha\) (全球節點)與 \(\beta\) (Isolate 級)都壓到極小。

V8 Isolate:共享進程的多租戶#

V8 是 Chrome 的 JavaScript 引擎。Isolate 是 V8 中一個獨立的虛擬機實例,擁有自己的 heap 與 garbage collector,但不綁定執行緒——V8 的設計原本就是為了「一個瀏覽器進程裡同時跑多個分頁的腳本」。

Cloudflare 把這個瀏覽器的設計搬到伺服器端:

┌───────────── 單一作業系統進程 ─────────────┐
│  Isolate A (租戶甲的Worker)                │
│  Isolate B (租戶乙的Worker)   ← 共享同一個 │
│  Isolate C (租戶丙的Worker)     V8 引擎、  │
│  Isolate D (租戶丁的Worker)     JIT、執行緒池│
└───────────────────────────────────────────┘

關鍵性質:

  1. 記憶體隔離但不需 OS 級隔離:JS 程式碼只能觸及自己 Isolate 的 heap,無法讀寫其他租戶的記憶體。
  2. 啟動成本近乎零:建立一個 Isolate 只是在既有的引擎上配置一段 heap,約幾毫秒以下;而 Cloudflare 更進一步——Worker 上傳時就先在全球節點預編譯並快取位元組碼,啟動時只剩 heap 配置:
項目 Lambda(VM) container V8 Isolate
隔離層級 硬體虛擬化 OS 核心帳號/namespace 語言引擎 heap 隔離
冷啟動 ~100ms~秒級 秒級 < 5ms
記憶體開銷/租戶 數十 MB 起 數十~數百 MB < 3 MB 起
單機租戶密度 低 中 高(數千以上)
可執行語言 任意 任意 JS/TS/WASM

密度高的代價是 CPU 時限:因為所有租戶公平共享 CPU,Workers 規定每次請求的 CPU 時間上限——免費方案約 10ms CPU 時間,付費方案(Bundled/Standard)可到 30 秒甚至更多。請分清:這是 CPU 時間而非掛鐘時間(wall time),等待 fetch() 或資料庫回應的 I/O 時間不計入:

\[ \text{耗用 CPU} = \int t_{\text{計算}}\,dt \neq \text{總耗時} \]

所以一個對 external API 等待 500ms 的 Worker,若自身計算只用 3ms,仍在免費額度內。

  1. 背後還有沙箱:V8 heap 隔離之外,Cloudflare 又層疊了 cgroups、seccomp 與自家檢查,工件是「Isolate 快 + 系統沙箱穩」的混合。

冷啟動歸零:兩個工程手段#

手段一:預先部署#

Worker 部署時,Cloudflare 同步把編譯後的腳本推送到所有資料中心。請求永遠在本地節點找到已就緒的程式碼,避免「跨區載入」:

\[ P(\text{本地命中}) \to 1 \]

手段二:Isolate 暫存與重用它#

節點上已存在相同腳本的暖 Isolate 時,直接重用(同一 Isolate 可以連續處理多個請求,如同瀏覽器一頁多幀)。因此實際觀測到的分佈近似:

\[ f(t_{\text{start}}) \approx \delta(t) \]

也就是說,對絕大多數請求,啟動時間趨近於零——這就是「冷啟動歸零」的工程含義。

安心地大膽使用#

因為 Isolate 小而便宜,Workers 鼓勵「每個請求都重新開始」的無狀態設計:不要在全域快取跨請求的暖狀態(會洩漏租戶資料),改用 KV、Durable Objects 等真正的共享儲存。

小結與練習#

  • Workers(2017)的賣點是「邊緣 + Isolate」:把 \(\alpha\) (距離)與 \(\beta\) (啟動)同時壓縮。
  • V8 Isolate 用語言引擎層的 heap 隔離換取極低的建立成本,與 VM 的硬體級隔離是不同級別的取捨。
  • CPU 時限是 Isolate 密度的代價,10ms/30s 上限約束的是計算而非等待。

練習:

  1. 用 wrangler dev 跑一個 Worker,在裡面放 Date.now() 前後差值,觀察一段「計算」程式與一段「await fetch 空網址」的耗時差異,驗證 CPU 時間 ≠ 總時間。
  2. 比較 Node.js node --v8-isolate(或直接用 vm 模組)與 Workers 的隔離語意,寫下三點差異。
  3. 假設你的 API 有 90% 的 CPU 時間花在 JSON 序列化,討論在 10ms 上限下你會怎麼瘦身回應。