iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
Software Development

30 天從 Full-Stack Engineer 進化到 System Design:從 0 設計可支撐百萬使用者的系統系列 第 4

# Day 4|Load Balancer 是什麼?多台 Server 到底要怎麼分配 Request?

  • 分享至 

  • xImage
  •  

Day 3 我們介紹了兩種基本的 Scaling 方法:

Vertical Scaling   → Scale Up
Horizontal Scaling → Scale Out

當一台 Server 不夠用時,我們可以使用 Horizontal Scaling 增加更多 Server:

Server #1
Server #2
Server #3

但新的問題馬上出現:

User 的 Request 到底要送去哪一台 Server?

如果 10,000 個 Request 同時進來,我們不希望全部都跑到 Server #1,也不可能讓 Client 自己決定要連哪一台 Server。

因此,我們需要一個 Component 放在 Client 和 Backend Servers 中間:

                     ┌── Server #1
                     │
Users → Load Balancer├── Server #2
                     │
                     └── Server #3

這就是今天的主題:

Load Balancer(負載平衡器)


Load Balancer 是什麼?

Load Balancer 最基本的工作可以理解成:

接收 Client 的 Request,然後把 Request 分配到後面的多台 Server。

假設我們有三台 Backend Server:

Server #1
Server #2
Server #3

如果沒有 Load Balancer,Client 必須知道每一台 Server 的位置:

User #1 → Server #1
User #2 → Server #2
User #3 → Server #3

這顯然很難管理。

加入 Load Balancer 後:

Users
  ↓
Load Balancer
  ↓
┌───────────────┐
↓       ↓       ↓
Server  Server  Server
 #1      #2      #3

Client 只需要知道:

Load Balancer

後面到底有幾台 Server,通常不需要由 Client 自己處理。


為什麼需要 Load Balancer?

最直接的原因就是:

Horizontal Scaling 之後,我們需要有人負責分配 Traffic。

假設有:

9 Requests

以及:

3 Servers

理想情況可能是:

Server #1 → 3 Requests
Server #2 → 3 Requests
Server #3 → 3 Requests

而不是:

Server #1 → 9 Requests 😵
Server #2 → 0 Requests
Server #3 → 0 Requests

Load Balancer 的目的之一,就是盡量避免 Traffic 不合理地集中在單一 Server。


Load Balancer 在 Architecture 的哪裡?

我們目前的架構是:

User
 ↓
Backend Server
 ↓
Database

Horizontal Scaling 後:

              ┌── Server #1
User ─────────┼── Server #2
              └── Server #3

加入 Load Balancer:

                     ┌── Server #1
                     │
User → Load Balancer ├── Server #2
                     │
                     └── Server #3
                              ↓
                          Database

所以 Load Balancer 通常會位在:

Client
  ↓
Load Balancer
  ↓
Backend Servers

它就像 Backend Servers 前面的「Traffic Controller」。


第一種方法:Round Robin

現在問題變成:

Load Balancer 到底要怎麼決定 Request 要去哪一台 Server?

最簡單的方法之一就是:

Round Robin

假設有三台 Server:

Server #1
Server #2
Server #3

Request 依序進來:

Request #1
Request #2
Request #3
Request #4
Request #5
Request #6

Round Robin 可以依序分配:

Request #1 → Server #1
Request #2 → Server #2
Request #3 → Server #3

Request #4 → Server #1
Request #5 → Server #2
Request #6 → Server #3

也就是:

#1 → #2 → #3 → #1 → #2 → #3 → ...

概念非常簡單。


Round Robin 的優點

Round Robin 最大的優點就是:

簡單,而且容易實作。

如果每台 Server:

  • Hardware 差不多
  • 處理能力差不多
  • 每個 Request 的工作量差不多

Round Robin 可以是一個很合理的方法。

例如:

Load Balancer
     │
     ├──→ Server #1
     ├──→ Server #2
     ├──→ Server #3
     ├──→ Server #1
     └──→ ...

Round Robin 的問題

但是現實世界中的 Request 不一定都一樣。

假設:

Request A → 10 ms
Request B → 20 ms
Request C → 5 seconds

Request C 可能需要做非常複雜的計算。

這時候 Server #1 可能已經很忙:

Server #1 → 100 active connections
Server #2 → 10 active connections
Server #3 → 5 active connections

如果 Load Balancer 還是單純按照順序:

#1 → #2 → #3 → #1

下一個 Request 還是有可能被送到已經非常忙的 Server #1。

所以我們可能需要更聰明的方法。


Least Connections

另一種常見策略叫做:

Least Connections

概念是:

把新的 Request / Connection 優先送給目前 Active Connections 較少的 Server。

假設:

Server #1 → 100 connections
Server #2 → 30 connections
Server #3 → 10 connections

下一個 Connection 可以優先送到:

Server #3

因為它目前的 Connection 最少。

                    Server #1 → 100
                   /
Load Balancer ─── Server #2 → 30
                   \
                    Server #3 → 10 ← New Connection

相較於單純 Round Robin,Least Connections 會考慮 Server 當下的 Connection Load。


Weighted Round Robin

還有另一種情況:

假設我們的 Server Hardware 不一樣。

Server #1
CPU: 16 cores
RAM: 64 GB

Server #2
CPU: 8 cores
RAM: 32 GB

Server #3
CPU: 4 cores
RAM: 16 GB

如果全部使用一樣的 Round Robin:

33% → Server #1
33% → Server #2
33% → Server #3

可能不太合理。

因為 Server #1 明顯比較強。

這時候可以使用:

Weighted Round Robin

例如設定:

Server #1 → Weight 4
Server #2 → Weight 2
Server #3 → Weight 1

比較強的 Server 就可以接收更多 Traffic。

概念上可能像:

Server #1 → ████
Server #2 → ██
Server #3 → █

所以 Load Balancing 並不是只有:

平均分配

還可以根據 Server 的 Capacity 做不同分配。


Load Balancer 怎麼知道 Server 掛掉了?

現在假設我們有:

Server #1 ✅
Server #2 ❌
Server #3 ✅

如果 Load Balancer 不知道 Server #2 已經掛掉,還是繼續把 Request 送過去:

Request
   ↓
Load Balancer
   ↓
Server #2 ❌

這些 Request 就會失敗。

所以 Load Balancer 還需要一個非常重要的功能:

Health Check


Health Check

Load Balancer 可以定期確認 Backend Server 是否健康。

例如 Server 提供:

GET /health

正常時回傳:

HTTP/1.1 200 OK

Load Balancer 就知道:

Server #1 → Healthy ✅

如果 Server #2:

  • 沒有 Response
  • Timeout
  • Connection Failed
  • Health Check 連續失敗

Load Balancer 可以暫時把它標記成:

Server #2 → Unhealthy ❌

然後停止把新的 Traffic 送給它。

Architecture:

                     ┌── Server #1 ✅
                     │
User → Load Balancer ├── Server #2 ❌
                     │
                     └── Server #3 ✅

新的 Request 只會送到:

Server #1
Server #3

這也是 Horizontal Scaling 能改善 Availability 的重要原因之一。


Server 恢復之後呢?

假設 Server #2 維修完成:

Server #2 ❌
     ↓
Restart
     ↓
Server #2 ✅

Load Balancer 的 Health Check 再次成功後,可以把 Server #2 加回 Backend Pool:

                     ┌── Server #1 ✅
                     │
User → Load Balancer ├── Server #2 ✅
                     │
                     └── Server #3 ✅

之後 Server #2 就可以重新接收 Traffic。

這讓系統具有一定程度的:

Fault Tolerance

也就是某個 Component 發生問題時,整個系統仍有能力繼續提供服務。


Load Balancer 還可以做什麼?

除了分配 Traffic,實際上的 Load Balancer 還可能負責很多事情,例如:

  • TLS Termination
  • Health Check
  • Routing
  • Sticky Sessions
  • Connection Management
  • Logging
  • Metrics

例如:

https://example.com/api/users

可能送到:

User Service

而:

https://example.com/api/orders

可能送到:

Order Service

不過 Day 4 先把最重要的核心概念掌握:

Load Balancer 最主要的角色,就是把 Traffic 分配到後面的多個 Server,並避免把 Request 送到不健康的 Server。


Layer 4 vs Layer 7 Load Balancing

Load Balancer 還常常會看到:

Layer 4 Load Balancer
Layer 7 Load Balancer

這裡先建立基本概念。

Layer 4

Layer 4 主要根據 Transport Layer 的資訊進行 Load Balancing,例如:

IP Address
Port
TCP / UDP

它不一定需要理解 HTTP Request 裡面的 URL Path。

簡化理解:

Client
  ↓
IP + Port
  ↓
Layer 4 Load Balancer
  ↓
Server

Layer 7

Layer 7 Load Balancer 可以理解 Application Layer 的資訊,例如 HTTP:

URL
Path
Header
Host
Cookie

所以它可以做更進階的 Routing。

例如:

/api/users
     ↓
User Service
/api/orders
     ↓
Order Service

Architecture:

                       ┌── /users  → User Service
User → L7 Load Balancer│
                       └── /orders → Order Service

因此 Layer 7 Load Balancer 可以根據 Application-Level Information 做更細緻的 Routing。

Day 4 不需要先把 OSI Model 全部背起來。

先記:

Layer 4
→ IP / Port / TCP / UDP

Layer 7
→ HTTP / URL / Header / Cookie

Sticky Session 是什麼?

Day 3 提到 Stateful Server 的問題。

假設 Alvin 登入後,Session 存在:

Server #1

第一次 Request:

Alvin
  ↓
Load Balancer
  ↓
Server #1

但第二次:

Alvin
  ↓
Load Balancer
  ↓
Server #2

如果 Session 只存在 Server #1,Server #2 可能找不到 Alvin 的 Session。

其中一種解法叫:

Sticky Session / Session Affinity

也就是:

Alvin → Server #1

之後 Alvin 的 Request 盡量都送到 Server #1。

例如:

Alvin ───→ Server #1
Bob   ───→ Server #2
John  ───→ Server #3

但 Sticky Session 也會產生新的問題。

例如 Server #1 掛掉:

Alvin → Server #1 ❌

原本存在 Server #1 的 State 可能就無法取得。

因此另一種常見思路是:

讓 Backend Server 盡量 Stateless。

把共享 State 放到:

Redis
Database
External Session Store

例如:

                     ┌── Server #1 ──┐
                     │               │
User → Load Balancer ├── Server #2 ──┼── Redis / DB
                     │               │
                     └── Server #3 ──┘

這樣 Request 被分配到不同 Server 時,也能取得共享的 State。


如果 Load Balancer 自己掛掉呢?

看到這裡會發現一個新的問題。

原本我們想解決:

Server = Single Point of Failure

所以加入:

Server #1
Server #2
Server #3

但是所有 Request 現在都先經過:

Load Balancer

如果只有一台 Load Balancer:

Users
  ↓
Load Balancer ❌
  ↓
Servers

即使後面的 Server 全部正常:

Server #1 ✅
Server #2 ✅
Server #3 ✅

User 還是可能無法正常存取服務。

所以:

Load Balancer 自己也可能成為 Single Point of Failure。


Load Balancer 也需要 High Availability

因此 Production System 通常不會希望整個服務只依賴單一 Load Balancer。

概念上可以設計成:

              ┌── Load Balancer #1
Users ────────┤
              └── Load Balancer #2
                       ↓
              ┌────────┼────────┐
              ↓        ↓        ↓
           Server    Server    Server
            #1        #2        #3

當其中一個 Load Balancer 發生問題時:

Load Balancer #1 ❌
Load Balancer #2 ✅

另一個仍然可以繼續提供服務。

實際上 Cloud Provider 通常會幫我們處理很多這類 High Availability 細節。

但從 System Design 的角度,最重要的是理解:

每加入一個新的 Component,都要問:如果它掛掉會發生什麼?

這是一個非常重要的 System Design 思考方式。


Real World:Cloud Load Balancer

在實際開發中,我們通常不會自己從零寫一個 Load Balancer。

Cloud Provider 已經提供 Managed Load Balancer。

例如在 AWS 裡,常見的 Load Balancer 包含:

Application Load Balancer (ALB)
Network Load Balancer (NLB)

可以先非常簡化地理解:

ALB
→ Layer 7
→ HTTP / HTTPS
→ 可以根據 Path / Host Routing
NLB
→ Layer 4
→ TCP / UDP / TLS
→ 適合需要高效能 Network Traffic 的情境

例如一個 Web Application:

Internet
   ↓
Application Load Balancer
   ↓
┌───────────────┐
↓       ↓       ↓
EC2     EC2     EC2

這就是非常常見的 Cloud Architecture。


Load Balancer 解決了什麼問題?

回到最開始。

我們原本只有:

User
 ↓
Server

Traffic 增加:

Traffic ↑
   ↓
Server 😵

所以使用 Horizontal Scaling:

Server #1
Server #2
Server #3

但新的問題是:

Request → Which Server?

因此加入:

Load Balancer

最後變成:

                     ┌── Server #1
                     │
User → Load Balancer ├── Server #2
                     │
                     └── Server #3

Load Balancer 幫助我們:

  1. Distribution:把 Traffic 分配給多台 Server。
  2. Scalability:讓我們可以增加更多 Backend Server。
  3. Availability:避免 Request 持續送到故障 Server。
  4. Health Check:確認 Backend Server 是否正常。
  5. Routing:根據不同規則決定 Request 去哪裡。

但是新的 Bottleneck 又出現了

現在 Architecture 已經從:

User
 ↓
Server
 ↓
Database

進化成:

                     ┌── Server #1
                     │
User → Load Balancer ├── Server #2
                     │
                     └── Server #3
                              ↓
                          Database

Backend Server 的 Scalability 提高了。

但是仔細看:

Server #1 ─┐
Server #2 ─┼──→ Database
Server #3 ─┘

所有 Server 還是連到:

同一個 Database

如果 Backend Server 從:

3 Servers

增加到:

100 Servers

可能會變成:

100 Backend Servers
        ↓
     Database
        😵

Backend 不再是 Bottleneck。

新的 Bottleneck 可能變成:

Database

這又回到前幾天一直出現的 System Design Pattern:

找到 Bottleneck
      ↓
解決 Bottleneck
      ↓
系統成長
      ↓
出現新的 Bottleneck
      ↓
再次調整 Architecture

這就是為什麼 System Design 是一個不斷做 Trade-off 的過程。


今天學到了什麼?

Day 4 最重要的概念是:

當我們使用 Horizontal Scaling 增加多台 Server 後,需要 Load Balancer 幫忙分配 Traffic。

最基本的 Architecture:

                     ┌── Server #1
                     │
User → Load Balancer ├── Server #2
                     │
                     └── Server #3

常見 Load Balancing Strategy:

Round Robin
Least Connections
Weighted Round Robin

Load Balancer 還需要:

Health Check

避免把 Traffic 送到:

Unhealthy Server ❌

另外也要記得:

Layer 4
→ IP / Port / TCP / UDP

Layer 7
→ HTTP / URL / Header / Cookie

最後最重要的 System Design 思考:

每加入一個新的 Component,都要問:它解決什麼問題?如果它自己掛掉又會發生什麼?


下一篇

現在我們已經可以:

User
 ↓
Load Balancer
 ↓
Multiple Backend Servers

但 Backend Server 最後仍然需要取得資料。

下一個非常重要的問題就是:

資料到底應該怎麼存?

例如:

User
Order
Payment
Post
Message

這些資料應該使用:

SQL?

還是:

NoSQL?

下一篇:

Day 5|SQL vs NoSQL:System Design 到底該選哪一種 Database?

我們會開始了解:

  • Relational Database 是什麼?
  • SQL 的 Table / Row / Relationship
  • NoSQL 是什麼?
  • Document Database 是什麼?
  • SQL 適合什麼情境?
  • NoSQL 適合什麼情境?
  • Consistency、Scalability 與 Data Model 的 Trade-off
  • 為什麼 System Design 不應該看到「大量資料」就直接選 NoSQL?

Architecture 也會從:

User → Load Balancer → Servers

開始正式進入:

User
 ↓
Load Balancer
 ↓
Backend Servers
 ↓
Database

也就是 System Design 裡另一個非常重要的世界:Data Layer


上一篇
# Day 3|Vertical Scaling vs Horizontal Scaling:Server 不夠用了怎麼辦?
下一篇
# Day 5|SQL vs NoSQL:System Design 到底該選哪一種 Database?
系列文
30 天從 Full-Stack Engineer 進化到 System Design:從 0 設計可支撐百萬使用者的系統9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言