Scalability Patterns
Interview Context
「一台 server 撐不住了,你會怎麼 scale?」是 system design 面試的開場經典題。面試官想看的不是背架構圖,而是你能不能講出每一步是什麼東西先壞掉、你加了什麼元件解決、又引入了什麼新問題。這一頁走完 single server 到 multi-region 的完整演化路徑,也是 DS/MLE 談 model serving scaling 的共同語言。
What You Should Understand
- 能完整敘述 single server → LB + 多台 app server → cache/CDN → replication → sharding → multi-region 的演化,以及每一步的動機
- 知道 L4 與 L7 load balancing 的差異,能比較 round robin、least connections、IP hash、consistent hashing
- 能解釋為什麼 stateless service 是 horizontal scaling 的前提,session 該externalize 到哪裡
- 掌握 database scaling path:read replica → cache → partitioning → sharding,以及 replication lag 和 hot shard 的處理
- 能設計流量尖峰的防禦:autoscaling、queue-based load leveling、load shedding、circuit breaker
- 知道要監控什麼:throughput、latency percentiles(p50/p95/p99)、error rate、saturation(USE/RED)
From One Server to Millions of Users
The classic scaling journey: start with everything on one box, and at each stage identify what breaks first and what you add to fix it. 面試時照這個順序講,每一步都說清楚「痛點 → 解法 → 新的 tradeoff」,就是一個結構完整的答案。
| Stage | What Breaks | What You Add | New Problem Introduced |
|---|---|---|---|
| 1. Single server | Web app + DB 搶同一台機器的 CPU/RAM/IO | Nothing yet — 這是起點 | Everything is a single point of failure |
| 2. Separate DB server | DB 查詢吃掉 app 的資源 | Dedicated database server | App 和 DB 各自仍是 SPOF |
| 3. LB + multiple app servers | 單台 app server 的 CPU 飽和;掛了就全站停擺 | Load balancer + N stateless app servers | Session 不能存在單台機器上(見 stateless 章節) |
| 4. Cache + CDN | DB 被重複的 read query 打爆;靜態資源吃頻寬 | Redis/Memcached cache layer + CDN for static assets | Cache invalidation、cache-DB consistency |
| 5. DB replication | 單台 DB 的 read throughput 到頂 | Primary-replica replication(write 走 primary,read 走 replicas) | Replication lag → stale reads |
| 6. Sharding | 單台 DB 的 write throughput / 資料量到頂 | Horizontal sharding by key | Cross-shard join/transaction 困難、hot shard、resharding 痛苦 |
| 7. Multi-region | 跨洲 latency 高、單一 region 掛掉全球停擺 | Geo-DNS routing + per-region deployment + cross-region data replication | Data consistency across regions、failover 複雜度 |
幾個貫穿全程的原則:
- Scale out, not up(優先 horizontal scaling):vertical scaling(換更大的機器)簡單但有硬上限,而且沒有 redundancy;horizontal scaling(加更多台)理論上無上限,但要求服務 stateless。
- 每一層都要消除 SPOF:LB 要有 failover、DB 要有 replica、cache 要有 cluster。一個元件只有一台,它就是你的 availability 上限。
- Async where possible:耗時的工作(發 email、產報表、跑 inference batch)丟進 message queue,web tier 保持快速回應。
面試敘事技巧
不要一開口就畫 multi-region 大架構。從 single server 講起,讓面試官看到你理解為什麼需要每個元件 — 「我們先假設 QPS 很低,一台就夠;當 read 開始變慢,我會先加 cache 而不是直接 shard,因為 sharding 的複雜度成本高很多。」這種 cost-aware 的推理比背圖高分。
Load Balancing Deep Dive
A load balancer distributes incoming requests across multiple servers, providing scalability (add more servers) and availability (route around dead servers).
L4 vs L7
| Aspect | L4 (Transport Layer) | L7 (Application Layer) |
|---|---|---|
| Routing based on | IP address + TCP/UDP port | HTTP headers, URL path, cookies, request body |
| Sees request content | No — 只轉發 TCP packets | Yes — 完整解析 HTTP request |
| Speed | Faster, lower overhead | Slower(要 terminate 連線、解析 protocol) |
| Capabilities | Basic distribution | Path-based routing(/api → service A), TLS termination, compression, rate limiting |
| Examples | AWS NLB, LVS, HAProxy (TCP mode) | AWS ALB, Nginx, HAProxy (HTTP mode), Envoy |
實務上常見兩層並用:edge 用 L4 撐超高吞吐,往內用 L7 做 path routing 和 TLS termination。
Load Balancing Algorithms
| Algorithm | How It Works | Pros | Cons | Best For |
|---|---|---|---|---|
| Round robin | 依序輪流分給每台 server | 簡單、無狀態 | 假設每台等強、每個 request 等重 | Homogeneous servers, uniform requests |
| Weighted round robin | 依 server 權重按比例分配 | 支援不同機型混用 | 權重要手動調 | 新舊機器混雜、canary 分流 |
| Least connections | 分給目前 active connection 最少的 server | 自動適應 request 耗時不均 | 要追蹤 connection 狀態 | Long-lived / 耗時差異大的 requests(如 model inference) |
| IP hash | hash(client IP) 決定 server | 同一 client 固定落同一台 | 分佈可能不均;NAT 後大量 client 同 IP | 需要 session affinity 又不想用 cookie |
| Consistent hashing | Server 排在 hash ring 上,key 順時針找最近節點 | 增減節點只影響 1/N 的 keys | 實作較複雜;需 virtual nodes 平衡 | Cache clusters、sharded storage、stateful routing |
Consistent hashing 為什麼重要:naive 的 hash(key) mod N 在 N 改變時幾乎所有 key 都要搬家 — 對 cache cluster 來說等於一次全面 cache miss storm。Consistent hashing 讓加減一台機器時,平均只有 的 keys 需要 remap。搭配 virtual nodes(每台實體機在 ring 上放多個點)可以讓分佈更均勻。
Health Checks
LB 必須知道後端哪些 server 還活著:
- Passive:觀察真實流量,連續失敗就標記 unhealthy。
- Active:LB 定期打 health endpoint(如
GET /healthz),連續 N 次失敗 → 移出 rotation;恢復 M 次成功 → 加回來。 - Health endpoint 應該檢查服務真的能工作(DB 連線、關鍵依賴),而不是只回 200 — 但也不能太重,避免 health check 本身變成負載。
Sticky Sessions vs Stateless
Sticky session(session affinity)讓同一個 user 的 requests 固定落在同一台 server,因為 session state 存在那台機器的記憶體。為什麼這是壞味道:
| Problem | Consequence |
|---|---|
| Server dies | 那台上所有 user 的 session 全部消失 → 被強制登出 |
| Uneven load | 熱門 user 集中的 server 過載,LB 無法重新平衡 |
| Autoscaling 失效 | 縮容時砍掉哪台都會傷到某群 user;新機器接不到既有 user |
| Deploy 困難 | Rolling deploy 每重啟一台就掃掉一批 session |
正解是把 state externalize(下一節),讓任何 server 都能處理任何 request。
LB Failover
Load balancer 自己也不能是 SPOF:
| Mode | How | Tradeoff |
|---|---|---|
| Active-passive | Standby LB 透過 heartbeat 監控 active LB,掛掉時接手 virtual IP(VRRP/keepalived) | 簡單;平時 standby 資源閒置;failover 有數秒空窗 |
| Active-active | 多台 LB 同時服務(DNS round robin 或 anycast 分流) | 資源利用率高、無單點;設定與 state 同步較複雜 |
Stateless Services & Session Management
A stateless service keeps no client state between requests — 每個 request 自帶所有必要資訊,或者 state 存在外部共用儲存。這是 horizontal scaling 的解鎖鑰匙。
| Where to Keep Session State | Latency | Pros | Cons |
|---|---|---|---|
| App server memory | Fastest | 零依賴 | 綁死 sticky session,無法自由 scale |
| Redis / Memcached | ~1ms | 快、支援 TTL、所有 server 共享 | 多一個要維運的元件 |
| Database | ~5-10ms | Durable | 慢,session 讀寫量大時壓垮 DB |
| Client-side token (JWT) | 0(隨 request 帶上) | Server 完全 stateless、免查儲存 | Token 無法即時撤銷、payload 大小受限 |
Stateless 之後你獲得的能力:
- 任意加減機器:LB 可以把 request 丟給任何一台,autoscaling 縮容不會傷害任何 user。
- Rolling deploy / 快速換血:重啟、替換 server 對 user 無感。
- 容錯:一台掛掉,下一個 request 自然落到別台,user 頂多重試一次。
常見誤區
「Stateless」不代表系統沒有 state — state 永遠存在,只是從 compute tier 搬到專門的 storage tier(Redis、DB、object storage)。面試時說「我把 state externalize 到 Redis,讓 web tier stateless」比說「我做成 stateless」精確得多。同樣的原則適用於 ML serving:model weights 是 read-only artifact 可以每台各載一份,但 user context / feature cache 要放外部共享儲存。
Database Scaling Path
Database 通常是最後、也最難 scale 的元件,因為它有 state。依複雜度由低到高:
Step 1: Read Replicas
Primary 負責所有 writes,changes 非同步複製到多台 read replicas,read query 分散到 replicas。適合 read-heavy workload(多數 web 應用 read:write 約 10:1 或更高)。
Replication lag 是主要代價:replica 落後 primary 數十 ms 到數秒。經典 bug — user 更新個人資料(write 到 primary)後立刻重新整理頁面(read 到 replica),看到舊資料。
| Mitigation | How |
|---|---|
| Read-your-writes | 該 user 寫入後的短時間內,他的 read 強制走 primary |
| Sticky read after write | Session 記錄最後 write 的 timestamp/LSN,read 只走已追上的 replica |
| Synchronous replication | 等至少一台 replica 確認才 commit — 犧牲 write latency 換一致性 |
| 業務容忍 | 明確標示「更新可能延遲數秒」— 很多場景其實可以接受 |
Step 2: Caching Layer
在 DB 前面擋一層 Redis/Memcached。Cache-aside 是最常見的 pattern:先查 cache,miss 才查 DB 並回填。Hit rate 90% 意味著 DB 只承受 10% 的 read 流量。相關細節(eviction、invalidation、stampede)屬於 caching 專章,這裡的重點是:在考慮 sharding 之前,先確認 cache 已經把 read 壓力吃掉。
Step 3: Vertical Partitioning
把不同的 tables / columns 拆到不同 DB:orders 一台、users 一台、logs 一台;或把巨大的 blob column 拆出去。按功能切割,實作直觀,但單一 table 太大的問題仍在。
Step 4: Horizontal Sharding
同一張 table 的 rows 按 shard key 切到多台 DB。這是 write scaling 的最終手段,代價最高:
| Challenge | Why It Hurts | Mitigation |
|---|---|---|
| Choosing shard key | 選錯 key 之後幾乎不可逆 | 選 cardinality 高、access pattern 對齊的 key(如 user_id) |
| Cross-shard queries | Join / aggregation 要 fan-out 到所有 shards 再合併 | Denormalize、預先聚合、或改用 analytics DB |
| Cross-shard transactions | 失去單機 ACID | Saga pattern、業務上避免跨 shard 交易 |
| Hot shard | 某個 key 流量特別大(celebrity problem) | 見下方 |
| Resharding | 資料搬遷期間要雙寫或停機 | 一開始就用 consistent hashing 或預切大量 virtual shards |
Hot shard mitigation:
- 更細的 shard key:
user_id改成hash(user_id + date)之類的 composite key,把單一熱點打散。 - Salting:對已知的熱 key 加隨機 suffix 分散到多個 shards,讀取時聚合。
- 熱點獨立 + cache:celebrity 帳號單獨隔離資源,並用 cache 吸收絕大部分 read。
- Virtual shards:邏輯上先切 1024 個 virtual shards,物理機器只是 virtual shards 的宿主,搬遷時以 virtual shard 為單位移動。
不要過早 sharding
面試中直接跳到 sharding 是常見扣分點。正確的順序是:cache → read replica → vertical partitioning → 真的不行才 sharding。一台現代 DB server 配合好的 index 和 cache 可以撐到每秒數萬 queries — 多數公司終其一生不需要 shard。展現你知道 sharding 的代價,比展現你會 shard 更值錢。
Nginx & Reverse Proxies in Practice
A reverse proxy sits in front of backend servers and handles incoming traffic on their behalf. Nginx 是最常見的實作,面試常被問「為什麼 Nginx 這麼快」。
Event-driven, non-blocking architecture:傳統 Apache(prefork 模式)一個 connection 配一個 process/thread,一萬個併發連線就要一萬個 threads — memory 和 context switch 成本爆炸(the C10K problem)。Nginx 用少量 worker processes(通常等於 CPU 核心數),每個 worker 跑一個 event loop,用 epoll/kqueue 非阻塞地同時看管數萬條 connections。連線在等待 IO 時不佔用 thread,所以記憶體開銷極低、吞吐極高。
Typical roles of Nginx in a deployment:
| Role | What It Does | Why Offload to Nginx |
|---|---|---|
| Static file serving | 直接回 images/JS/CSS,不打到 app | Nginx serve 靜態檔比 Python/Node app 快一個量級 |
| TLS termination | 對外 HTTPS,對內解密成 HTTP | 集中管理憑證;app servers 免除加解密 CPU 成本 |
| Reverse proxy | 轉發 request 到 upstream app servers,隱藏內部拓撲 | 安全(後端不直接暴露)、可加 buffer/timeout/retry |
| Load balancing | upstream block 內建 round robin / least_conn / ip_hash | 一個設定檔就有 L7 LB |
| Compression / caching | gzip response、cache 可快取的回應 | 減少頻寬與後端負載 |
| Rate limiting | limit_req 限制單一 client 的請求速率 | 第一道防濫用防線 |
# Minimal Nginx reverse proxy + load balancer
upstream app_servers {
least_conn; # pick server with fewest active connections
server 10.0.0.11:8000 weight=3; # newer, bigger machine
server 10.0.0.12:8000 weight=1;
server 10.0.0.13:8000 backup; # only used when others are down
}
server {
listen 443 ssl;
location /static/ {
root /var/www; # serve static files directly
}
location / {
proxy_pass http://app_servers;
proxy_next_upstream error timeout; # retry next server on failure
}
}
Forward proxy vs reverse proxy 一句話區分:forward proxy 代表 client 出門(隱藏 client,如公司網路出口);reverse proxy 代表 server 接客(隱藏 servers,做 LB/TLS/快取)。
Handling Traffic Spikes
平常 1000 QPS 的系統遇到行銷活動打進 20000 QPS,怎麼不倒?分四層防禦。
Autoscaling
根據 metrics 自動加減 instances:
| Policy | Trigger | Notes |
|---|---|---|
| Target tracking | 維持某 metric 在目標值(如 CPU 60%) | 最常用;設好目標就自動調 |
| Step scaling | Metric 超過閾值 → 加固定數量 | 可分級:CPU 70% 加 2 台,90% 加 5 台 |
| Scheduled | 依已知模式預先擴容 | 每天晚餐時段、已排定的行銷活動 |
| Predictive | ML 預測未來負載提前擴容 | 適合有規律週期的流量 |
Autoscaling 的限制要講出來才專業:新 instance 冷啟動要 1-5 分鐘(開機、載入 model、暖 cache),瞬間尖峰等不了;scale-in 要設 cooldown 避免震盪;下游(DB)不會跟著自動變大 — app tier 擴容可能只是把壓力更快地灌進 DB。
Queue-Based Load Leveling
在 producer 和 consumer 之間放一個 message queue(Kafka/SQS/RabbitMQ),把瞬間尖峰「攤平」成 consumer 可消化的穩定速率。適用於可以非同步的工作:下單後的 email、影片轉檔、batch inference。User 得到「已受理」的即時回應,實際處理稍後完成。代價是 end-to-end latency 變長,且要監控 queue depth — queue 一直長就是 consumer 追不上。
Graceful Degradation & Load Shedding
當流量超過極限,有選擇地犧牲,保住核心:
- Graceful degradation:關掉非核心功能 — 推薦欄位改回熱門榜(不打 model)、暫停即時通知、搜尋改用簡化 ranking。系統變「笨」但不倒。
- Load shedding:主動拒絕超額請求(回 429 Too Many Requests),優先砍低優先級流量(爬蟲、預載、免費用戶),保住付費核心操作。快速拒絕 1% 的 requests,遠比 100% 的 requests 全部 timeout 好。
- Backpressure:下游明確告訴上游「慢一點」,而不是默默累積 queue 直到 OOM。
Circuit Breakers
防止一個掛掉的依賴拖垮整個系統。Circuit breaker 包在對外部服務的呼叫外面,有三個狀態:
| State | Behavior | Transition |
|---|---|---|
| Closed | 正常放行,統計失敗率 | 失敗率超過閾值 → Open |
| Open | 直接 fail fast(回 fallback),不打依賴 | 等待 timeout 後 → Half-open |
| Half-open | 放少量試探請求 | 成功 → Closed;失敗 → Open |
沒有 circuit breaker 的災難劇本:依賴的服務變慢 → 你的 threads 全部卡在等它 timeout → 你的服務也沒有可用 thread → 你的上游也卡住 → cascading failure 一路往上燒。Fail fast + fallback(回 cache 值、預設值、降級結果)切斷連鎖。
面試加分句
「Autoscaling 處理的是可預期的緩慢增長,load shedding 和 circuit breaker 處理的是擴容來不及的瞬間尖峰和依賴故障 — 兩者是互補的,不是二選一。」能講出這個層次感,面試官會知道你真的 operate 過系統。
System Performance Metrics
Scale 的前提是知道瓶頸在哪。兩個經典 framework:
RED Method (for request-driven services)
| Metric | What | Alert Example |
|---|---|---|
| Rate | Requests per second (throughput) | QPS 突降 50% — 上游掛了? |
| Errors | 失敗請求數 / 比率 | Error rate 超過 1% |
| Duration | Latency 分佈(percentiles) | p99 超過 500ms |
USE Method (for resources: CPU, memory, disk, network)
| Metric | What | Example |
|---|---|---|
| Utilization | 資源忙碌時間比例 | CPU 85% busy |
| Saturation | 排隊中的工作量 | Run queue length、connection pool 等待數 |
| Errors | 資源層級錯誤 | Disk IO errors、dropped packets |
Why Percentiles, Not Averages
Average latency 會騙人:99 個 request 花 10ms、1 個花 2000ms,平均約 30ms 看起來很棒,但那 1% 的 user 體驗極差。所以看 p50(典型體驗)/ p95 / p99(尾部體驗)。尾部很重要,因為:(1)大流量下 1% 就是每天數十萬個受害 request;(2)一個頁面 fan-out 打 10 個後端服務時,只要任何一個踩到 p99,整頁就慢 — 你的 page-level p50 由 service-level p99 決定(tail amplification)。
另外兩個關鍵觀念:
- Throughput 和 latency 的關係不是線性:utilization 接近 100% 時,queueing 效應讓 latency 急遽飆升(排隊理論)。所以 capacity planning 通常以 60-75% utilization 為目標水位,留 headroom 吸收尖峰和機器故障。
- Capacity planning 基本流程:量測單台 instance 的極限(load test 找出 latency 開始劣化的 QPS)→ 用 peak traffic 估算需要的台數 → 加上 redundancy(N+2:容忍 1 台故障 + 1 台在 deploy)→ 對照流量成長率排採購/擴容時程。
Real-World Use Cases
Case 1: 模型推論服務從單機到 Autoscaling 集群
你訓練好一個 fraud detection model,用 FastAPI 包成 API 部署在一台機器上,p99 latency 40ms。行銷部門接了一個大客戶,流量預估成長 20 倍。
這一頁的概念怎麼用上:
- Model server 天然接近 stateless(weights 是 read-only),把 feature cache 移到 Redis 之後就完全 stateless → 可以放到 LB 後面 horizontal scaling。
- Inference latency 因輸入而異(有些交易要查更多 features),所以 LB 用 least connections 而不是 round robin,避免慢 request 堆在同一台。
- Autoscaling 用 target tracking(GPU/CPU utilization 70%),但要注意冷啟動:新 instance 載入 model 要 90 秒,所以配 scheduled scaling 在已知高峰前預先擴容。
- 過載時的 graceful degradation:model service 超時就 fallback 到 rule-based score,寧可精度略降也不能 block 交易。
Interview follow-ups:
- 你的 health check endpoint 應該檢查什麼?只回 200 夠嗎?(要驗證 model 已載入且能推論,例如對固定樣本跑一次 forward pass)
- Model 更新時怎麼 rolling deploy 不中斷服務?如果新舊版本的 feature schema 不同呢?
- GPU instance 很貴,autoscaling 的 scale-in 策略怎麼設才不會震盪?
Case 2: 報表查詢壓垮主庫 — Read Replica 與 Cache
你是 DS,寫了一批 dashboard 查詢直接打 production PostgreSQL。月底主管們同時開報表,heavy aggregation queries 把 primary DB 的 CPU 打到 100%,線上交易開始 timeout — 你的 dashboard 弄倒了主站。
這一頁的概念怎麼用上:
- Analytics 流量和 transactional 流量必須隔離。第一步:加 read replica,所有報表查詢改連 replica — primary 只服務線上交易。
- Replication lag 對報表通常無害(晚 5 秒沒人在乎),這是 lag 容忍度高的完美場景。
- Dashboard 的查詢重複性極高 → 加 cache layer(或 pre-aggregated summary tables,每小時 refresh),同一份月報不需要每次重算。
- 長期解法:把分析負載搬去 columnar warehouse(BigQuery/Snowflake),OLTP 和 OLAP 徹底分家。
Interview follow-ups:
- 如果某個報表需要「絕對最新」的資料,read replica 的 lag 怎麼處理?
- Replica 也被打爆了怎麼辦?(加 replica、加 cache、限制查詢併發、改 pre-aggregation)
- 為什麼 OLTP database 跑 analytics query 特別傷?(row store 對 full scan aggregation 不友善、長交易阻擋 vacuum、buffer pool 被掃光)
Case 3: 行銷活動流量尖峰的 Load Shedding
電商平台辦限時搶購,開賣瞬間 QPS 從 2000 暴衝到 60000。Autoscaling 來不及(冷啟動 3 分鐘),去年同樣的活動把整站打掛,包含跟活動無關的頁面。
這一頁的概念怎麼用上:
- Scheduled scaling:活動時間已知,提前 30 分鐘擴容到預估容量的 1.5 倍 — predictive 比 reactive 便宜且可靠。
- Queue-based load leveling:搶購下單改成非同步 — request 進 queue 立刻回「排隊中」,consumer 以 DB 能承受的速率消化。User 等 3 秒拿到結果,遠好過整站 timeout。
- Load shedding 分級:超額流量按優先級砍 — 先拒爬蟲和未登入流量,再限縮瀏覽類 API,最後才動結帳流程。回 429 + Retry-After,讓 client 有秩序地重試。
- Circuit breaker 保護無關服務:推薦系統、評論服務等非核心依賴掛了就 fallback 靜態內容,不讓它們的故障擴散到搶購主流程。
- Cache 靜態化:活動頁面全部 CDN 化,商品詳情 cache TTL 拉長 — 只有庫存和下單需要打到後端。
Interview follow-ups:
- 搶購的庫存扣減怎麼避免 oversell?(Redis atomic decrement / DB row lock / 預扣庫存 + 非同步確認的 tradeoff)
- 怎麼區分「值得服務的尖峰」和「該擋掉的濫用流量」?
- 活動後怎麼做 capacity 覆盤?要看哪些 metrics 決定明年準備多少容量?
Hands-on: Load Balancing in Python
Weighted Round-Robin Balancer
class WeightedRoundRobin:
"""Smooth weighted round-robin (same algorithm as Nginx).
Each pick: add weight to each server's current score,
pick the highest, then subtract total weight from it.
Produces an evenly interleaved sequence like A A B A C.
"""
def __init__(self, servers):
# servers: dict of name -> weight, e.g. {"s1": 3, "s2": 1, "s3": 1}
self.weights = dict(servers)
self.current = {name: 0 for name in servers}
self.total = sum(servers.values())
def next_server(self):
for name, weight in self.weights.items():
self.current[name] += weight # accumulate score
best = max(self.current, key=self.current.get)
self.current[best] -= self.total # penalize the chosen one
return best
lb = WeightedRoundRobin({"s1": 3, "s2": 1, "s3": 1})
sequence = [lb.next_server() for _ in range(5)] # s1, s1, s2, s1, s3
Least-Connections Balancer
import heapq
import itertools
class LeastConnections:
"""Route each request to the server with the fewest active connections.
A heap keyed by (active_connections, tiebreaker) gives O(log n) picks.
Caller must call release() when the request finishes.
"""
def __init__(self, servers):
self.active = {name: 0 for name in servers}
self.counter = itertools.count() # tiebreaker for equal loads
def acquire(self):
# pick server with minimum active connections
name = min(self.active, key=lambda s: (self.active[s], next(self.counter)))
self.active[name] += 1
return name
def release(self, name):
self.active[name] -= 1 # request finished
lb = LeastConnections(["s1", "s2", "s3"])
server = lb.acquire() # dispatch request to this server
# ... request completes ...
lb.release(server)
Health Checker Loop
import time
import urllib.request
class HealthChecker:
"""Active health checks: probe /healthz, require consecutive
failures/successes before flipping state (avoids flapping)."""
def __init__(self, servers, fail_threshold=3, rise_threshold=2):
self.servers = servers # name -> base URL
self.healthy = {name: True for name in servers}
self.fails = {name: 0 for name in servers}
self.rises = {name: 0 for name in servers}
self.fail_threshold = fail_threshold
self.rise_threshold = rise_threshold
def probe(self, url):
try:
with urllib.request.urlopen(url + "/healthz", timeout=2) as resp:
return resp.status == 200
except Exception:
return False
def check_all(self):
for name, url in self.servers.items():
if self.probe(url):
self.fails[name] = 0
self.rises[name] += 1
if not self.healthy[name] and self.rises[name] >= self.rise_threshold:
self.healthy[name] = True # add back to rotation
else:
self.rises[name] = 0
self.fails[name] += 1
if self.healthy[name] and self.fails[name] >= self.fail_threshold:
self.healthy[name] = False # remove from rotation
def alive_servers(self):
return [name for name, ok in self.healthy.items() if ok]
def run(self, interval=5):
while True: # background loop
self.check_all()
time.sleep(interval)
Interview Signals
What interviewers listen for:
- 你按演化順序講架構(先 cache 再 replica 再 shard),而不是一開始就丟出過度設計的大圖
- 每加一個元件你都能說出它引入的新問題(replication lag、cache consistency、sticky session)
- 你知道 stateless 是 horizontal scaling 的前提,能具體說 state 要 externalize 到哪裡
- 你主動談 failure:SPOF 消除、health check、circuit breaker、load shedding、rollback
- 你用數字說話:latency percentiles 而非 average、utilization 目標水位、單機容量估算
Practice
Flashcards
Flashcards (1/10)
從一台 server 到百萬用戶,講出經典的七步演化。
(1)Single server →(2)DB 分離出去 →(3)LB + 多台 stateless app servers →(4)Cache + CDN →(5)DB read replication →(6)Sharding →(7)Multi-region。每一步的口訣:什麼先壞掉、加什麼、引入什麼新問題。
Quiz
單台 app server CPU 飽和且成為 SPOF。演化路徑的下一步是什麼?