负载均衡 #

一、负载均衡概述 #

1.1 什么是负载均衡 #

负载均衡是将请求分发到多个后端服务器的技术,用于:

  • 提高可用性
  • 提升性能
  • 实现扩展性
  • 故障转移

1.2 Varnish负载均衡架构 #

text
┌─────────────────────────────────────────────────────────┐
│                  Varnish 负载均衡架构                     │
├─────────────────────────────────────────────────────────┤
│                                                         │
│                    ┌─────────┐                          │
│                    │ Client  │                          │
│                    └────┬────┘                          │
│                         │                               │
│                    ┌────▼────┐                          │
│                    │ Varnish │                          │
│                    │ Load    │                          │
│                    │ Balancer│                          │
│                    └────┬────┘                          │
│                         │                               │
│         ┌───────────────┼───────────────┐               │
│         │               │               │               │
│    ┌────▼────┐    ┌────▼────┐    ┌────▼────┐          │
│    │Server 1 │    │Server 2 │    │Server 3 │          │
│    └─────────┘    └─────────┘    └─────────┘          │
│                                                         │
└─────────────────────────────────────────────────────────┘

1.3 支持的调度算法 #

算法 说明 适用场景
round_robin 轮询 服务器性能相近
fallback 故障转移 主备模式
hash 哈希 会话保持
random 随机 简单分发

二、directors模块 #

2.1 导入模块 #

vcl
import directors;

2.2 定义后端 #

vcl
backend server1 {
    .host = "192.168.1.10";
    .port = "80";
}

backend server2 {
    .host = "192.168.1.11";
    .port = "80";
}

backend server3 {
    .host = "192.168.1.12";
    .port = "80";
}

2.3 初始化负载均衡器 #

vcl
sub vcl_init {
    # 创建负载均衡器
    new cluster = directors.round_robin();
    cluster.add_backend(server1);
    cluster.add_backend(server2);
    cluster.add_backend(server3);
}

2.4 使用负载均衡器 #

vcl
sub vcl_recv {
    set req.backend_hint = cluster.backend();
}

三、轮询调度 #

3.1 基本配置 #

vcl
import directors;

backend server1 { .host = "192.168.1.10"; .port = "80"; }
backend server2 { .host = "192.168.1.11"; .port = "80"; }
backend server3 { .host = "192.168.1.12"; .port = "80"; }

sub vcl_init {
    new cluster = directors.round_robin();
    cluster.add_backend(server1);
    cluster.add_backend(server2);
    cluster.add_backend(server3);
}

sub vcl_recv {
    set req.backend_hint = cluster.backend();
}

3.2 工作原理 #

text
请求1 ──► Server 1
请求2 ──► Server 2
请求3 ──► Server 3
请求4 ──► Server 1
请求5 ──► Server 2
...

3.3 加权轮询 #

vcl
sub vcl_init {
    new cluster = directors.round_robin();
    # 高性能服务器添加多次
    cluster.add_backend(server1);
    cluster.add_backend(server1);
    cluster.add_backend(server2);
    cluster.add_backend(server3);
}

四、故障转移 #

4.1 基本配置 #

vcl
import directors;

backend primary {
    .host = "192.168.1.10";
    .port = "80";
}

backend secondary {
    .host = "192.168.1.11";
    .port = "80";
}

sub vcl_init {
    new cluster = directors.fallback();
    cluster.add_backend(primary);
    cluster.add_backend(secondary);
}

sub vcl_recv {
    set req.backend_hint = cluster.backend();
}

4.2 工作原理 #

text
正常情况:
请求 ──► Primary Server

Primary故障:
请求 ──► Secondary Server

Primary恢复:
请求 ──► Primary Server

4.3 多级故障转移 #

vcl
sub vcl_init {
    new cluster = directors.fallback();
    cluster.add_backend(primary);
    cluster.add_backend(secondary);
    cluster.add_backend(tertiary);
}

五、哈希调度 #

5.1 基本配置 #

vcl
import directors;

backend server1 { .host = "192.168.1.10"; .port = "80"; }
backend server2 { .host = "192.168.1.11"; .port = "80"; }
backend server3 { .host = "192.168.1.12"; .port = "80"; }

sub vcl_init {
    new cluster = directors.hash();
    cluster.add_backend(server1, 1.0);
    cluster.add_backend(server2, 1.0);
    cluster.add_backend(server3, 1.0);
}

sub vcl_recv {
    # 基于URL哈希
    set req.backend_hint = cluster.backend(req.url);
}

5.2 工作原理 #

text
URL /page1 ──► Hash(URL) ──► Server 1
URL /page2 ──► Hash(URL) ──► Server 2
URL /page1 ──► Hash(URL) ──► Server 1 (相同URL总是到同一服务器)

5.3 会话保持 #

vcl
sub vcl_recv {
    # 基于Cookie会话保持
    if (req.http.Cookie ~ "session_id") {
        set req.backend_hint = cluster.backend(
            regsub(req.http.Cookie, ".*session_id=([^;]+).*", "\1")
        );
    } else {
        set req.backend_hint = cluster.backend(req.url);
    }
}

5.4 基于IP哈希 #

vcl
sub vcl_recv {
    # 基于客户端IP哈希
    set req.backend_hint = cluster.backend(client.ip);
}

5.5 权重配置 #

vcl
sub vcl_init {
    new cluster = directors.hash();
    # 权重比例 2:1:1
    cluster.add_backend(server1, 2.0);
    cluster.add_backend(server2, 1.0);
    cluster.add_backend(server3, 1.0);
}

六、随机调度 #

6.1 基本配置 #

vcl
import directors;

backend server1 { .host = "192.168.1.10"; .port = "80"; }
backend server2 { .host = "192.168.1.11"; .port = "80"; }
backend server3 { .host = "192.168.1.12"; .port = "80"; }

sub vcl_init {
    new cluster = directors.random();
    cluster.add_backend(server1, 1.0);
    cluster.add_backend(server2, 1.0);
    cluster.add_backend(server3, 1.0);
}

sub vcl_recv {
    set req.backend_hint = cluster.backend();
}

6.2 加权随机 #

vcl
sub vcl_init {
    new cluster = directors.random();
    # 权重比例 3:2:1
    cluster.add_backend(server1, 3.0);
    cluster.add_backend(server2, 2.0);
    cluster.add_backend(server3, 1.0);
}

七、健康检查集成 #

7.1 后端健康检查 #

vcl
probe healthcheck {
    .url = "/healthcheck";
    .timeout = 2s;
    .interval = 5s;
    .window = 5;
    .threshold = 3;
}

backend server1 {
    .host = "192.168.1.10";
    .port = "80";
    .probe = healthcheck;
}

backend server2 {
    .host = "192.168.1.11";
    .port = "80";
    .probe = healthcheck;
}

backend server3 {
    .host = "192.168.1.12";
    .port = "80";
    .probe = healthcheck;
}

7.2 自动剔除故障服务器 #

负载均衡器会自动检测后端健康状态,不健康的后端会被自动剔除:

vcl
import directors;

sub vcl_init {
    new cluster = directors.round_robin();
    cluster.add_backend(server1);
    cluster.add_backend(server2);
    cluster.add_backend(server3);
}

# 当server2不健康时,请求会自动分发到server1和server3

八、动态后端管理 #

8.1 运行时添加后端 #

vcl
import dynamic;

sub vcl_init {
    new d = dynamic.director(
        port = "80",
        ttl = 10m
    );
}

sub vcl_recv {
    # 动态选择后端
    if (req.http.X-Backend-Host) {
        set req.backend_hint = d.backend(req.http.X-Backend-Host);
    }
}

8.2 基于DNS发现 #

vcl
import dynamic;

sub vcl_init {
    new d = dynamic.director(
        port = "80",
        ttl = 30s
    );
}

sub vcl_recv {
    # 使用DNS名称
    set req.backend_hint = d.backend("backend.example.com");
}

九、高级配置 #

9.1 多集群配置 #

vcl
import directors;

# Web服务器集群
backend web1 { .host = "192.168.1.10"; .port = "80"; }
backend web2 { .host = "192.168.1.11"; .port = "80"; }

# API服务器集群
backend api1 { .host = "192.168.2.10"; .port = "8080"; }
backend api2 { .host = "192.168.2.11"; .port = "8080"; }

sub vcl_init {
    new web_cluster = directors.round_robin();
    web_cluster.add_backend(web1);
    web_cluster.add_backend(web2);
    
    new api_cluster = directors.round_robin();
    api_cluster.add_backend(api1);
    api_cluster.add_backend(api2);
}

sub vcl_recv {
    if (req.url ~ "^/api/") {
        set req.backend_hint = api_cluster.backend();
    } else {
        set req.backend_hint = web_cluster.backend();
    }
}

9.2 基于内容类型分发 #

vcl
sub vcl_recv {
    if (req.url ~ "\.(css|js|png|gif|jpg|jpeg)$") {
        set req.backend_hint = static_cluster.backend();
    } elseif (req.url ~ "^/api/") {
        set req.backend_hint = api_cluster.backend();
    } else {
        set req.backend_hint = web_cluster.backend();
    }
}

9.3 基于地理位置分发 #

vcl
import geoip;

sub vcl_recv {
    if (geoip.country_code(client.ip) == "CN") {
        set req.backend_hint = china_cluster.backend();
    } elseif (geoip.country_code(client.ip) == "US") {
        set req.backend_hint = us_cluster.backend();
    } else {
        set req.backend_hint = global_cluster.backend();
    }
}

9.4 A/B测试 #

vcl
sub vcl_recv {
    if (req.http.Cookie ~ "variant=b") {
        set req.backend_hint = variant_b.backend();
    } else {
        set req.backend_hint = variant_a.backend();
    }
}

十、故障处理 #

10.1 重试机制 #

vcl
sub vcl_backend_response {
    # 后端错误时重试
    if (beresp.status >= 500 && bereq.retries < 3) {
        return (retry);
    }
}

10.2 超时处理 #

vcl
sub vcl_backend_error {
    # 记录错误
    std.log("Backend error: " + bereq.url);
    
    # 返回错误页面
    set beresp.status = 503;
    set beresp.http.Content-Type = "text/html";
    synthetic({"<!DOCTYPE html>
<html>
<head><title>Service Unavailable</title></head>
<body>
<h1>503 Service Unavailable</h1>
<p>Please try again later.</p>
</body>
</html>"});
    return (deliver);
}

10.3 熔断机制 #

vcl
import vsthrottle;

sub vcl_recv {
    # 检查后端是否熔断
    if (vsthrottle.is_denied("backend_" + req.backend_hint, 10, 10s)) {
        return (synth(503, "Backend circuit breaker open"));
    }
}

sub vcl_backend_response {
    if (beresp.status >= 500) {
        # 记录失败
        vsthrottle.add("backend_" + bereq.backend, 1);
    }
}

十一、监控与管理 #

11.1 查看后端状态 #

bash
# 查看所有后端
varnishadm backend.list

# 输出示例
name        ref   probe   health
server1     1     5/5     healthy
server2     1     5/5     healthy
server3     1     3/5     sick

11.2 后端统计 #

bash
# 查看后端连接统计
varnishstat -1 -f VBE.*

# 查看特定后端
varnishstat -1 -f VBE.server1.*

11.3 手动管理后端 #

bash
# 设置后端状态
varnishadm backend.set_health server1 sick
varnishadm backend.set_health server1 healthy
varnishadm backend.set_health server1 auto

11.4 监控脚本 #

bash
#!/bin/bash
# lb_monitor.sh

echo "=== Load Balancer Status ==="
echo ""

echo "Backend Health:"
varnishadm backend.list

echo ""
echo "Backend Connections:"
varnishstat -1 -f VBE.*.conn

echo ""
echo "Backend Requests:"
varnishstat -1 -f VBE.*.req

echo ""
echo "Backend Failures:"
varnishstat -1 -f VBE.*.fail

十二、完整配置示例 #

vcl
vcl 4.1;

import directors;
import std;

# 健康检查配置
probe healthcheck {
    .url = "/healthcheck";
    .timeout = 2s;
    .interval = 5s;
    .window = 5;
    .threshold = 3;
}

# Web服务器后端
backend web1 {
    .host = "192.168.1.10";
    .port = "80";
    .probe = healthcheck;
    .max_connections = 500;
}

backend web2 {
    .host = "192.168.1.11";
    .port = "80";
    .probe = healthcheck;
    .max_connections = 500;
}

backend web3 {
    .host = "192.168.1.12";
    .port = "80";
    .probe = healthcheck;
    .max_connections = 500;
}

# API服务器后端
backend api1 {
    .host = "192.168.2.10";
    .port = "8080";
    .probe = healthcheck;
    .max_connections = 300;
}

backend api2 {
    .host = "192.168.2.11";
    .port = "8080";
    .probe = healthcheck;
    .max_connections = 300;
}

# 初始化负载均衡器
sub vcl_init {
    # Web集群 - 轮询
    new web_cluster = directors.round_robin();
    web_cluster.add_backend(web1);
    web_cluster.add_backend(web2);
    web_cluster.add_backend(web3);
    
    # API集群 - 哈希(会话保持)
    new api_cluster = directors.hash();
    api_cluster.add_backend(api1, 1.0);
    api_cluster.add_backend(api2, 1.0);
}

# 请求处理
sub vcl_recv {
    # 根据URL选择集群
    if (req.url ~ "^/api/") {
        # API请求 - 基于会话保持
        if (req.http.Cookie ~ "session_id") {
            set req.backend_hint = api_cluster.backend(
                regsub(req.http.Cookie, ".*session_id=([^;]+).*", "\1")
            );
        } else {
            set req.backend_hint = api_cluster.backend(req.url);
        }
    } else {
        # Web请求 - 轮询
        set req.backend_hint = web_cluster.backend();
    }
    
    # 添加客户端IP
    set bereq.http.X-Forwarded-For = client.ip;
}

# 后端错误处理
sub vcl_backend_response {
    # 错误重试
    if (beresp.status >= 500 && bereq.retries < 3) {
        return (retry);
    }
}

sub vcl_backend_error {
    set beresp.status = 503;
    set beresp.http.Content-Type = "text/html; charset=utf-8";
    synthetic({"<!DOCTYPE html>
<html>
<head><title>Service Unavailable</title></head>
<body>
<h1>503 Service Unavailable</h1>
<p>The server is temporarily unavailable.</p>
</body>
</html>"});
    return (deliver);
}

十三、总结 #

本章我们学习了:

  1. 负载均衡概述:概念、架构、算法
  2. directors模块:导入和基本使用
  3. 轮询调度:round_robin配置
  4. 故障转移:fallback配置
  5. 哈希调度:hash配置、会话保持
  6. 随机调度:random配置
  7. 健康检查集成:自动剔除故障服务器
  8. 动态后端:运行时管理
  9. 高级配置:多集群、A/B测试
  10. 故障处理:重试、超时、熔断
  11. 监控管理:状态查看、手动管理

掌握负载均衡后,让我们进入下一章,学习缓存规则配置!

最后更新:2026-03-28