- 后端
- 网络/通信
- 云原生
【免费下载链接】bfe
A modern layer 7 load balancer from baidu
导读
本文围绕 BFE(Baidu Front End,百度开源的七层负载均衡器)的核心能力之一——**流量均衡(Traffic Balancing)**展开,系统讲解其两级的负载均衡体系:子集群级(Sub Cluster Level)与实例级(Instance Level)的调度策略、BLACKHOLE 黑洞集群、后端健康检查状态机、自动重试、连接池复用以及会话保持机制。通过阅读本文,你将掌握 BFE 在多 IDC 场景下如何按权重分配流量、如何配置健康检查与重试参数,并理解每一机制背后的源码实现与配置文件格式,可直接对照本仓库的配置样例落地实践。
一、BFE 流量均衡的整体架构
BFE 的流量均衡是分层的。从转发路径上看,一次请求先被路由到某个集群(Cluster),集群内部再按照以下两级完成后端选择:
- 子集群级(Sub Cluster Level):在集群的多个子集群之间分配流量,通常对应多 IDC 场景;
- 实例级(Instance Level):在子集群内部的多个后端实例之间分配流量。
这两级逻辑在源码中分别由两个包实现:
- 子集群级均衡:
bfe_balance/bal_gslb/(GSLB 调度,核心文件 bal_gslb.go); - 实例级均衡:
bfe_balance/bal_slb/(SLB 调度,核心文件 bal_rr.go)。
此外,bfe_balance/backend/目录承载后端实例的数据结构(bfe_backend.go)与健康检查逻辑(health_check.go),是两级均衡共同依赖的基础设施。
二、子集群级负载均衡(Sub Cluster Level Load Balance)
2.1 核心思想:按权重分配跨 IDC 流量
通常一个集群会包含多个子集群。在 BFE 中,可以为每个子集群定义权重,BFE 依据权重将流量分配到各个子集群。这一特性在多 IDC 场景下尤为重要:通过调整各 IDC 对应子集群的权重,即可在不改动代码的情况下灵活控制各机房承接的流量比例。
从源码看,子集群级均衡由BalanceGslb结构体承载(bal_gslb.go),其内部维护了:
subClusters:子集群列表;totalWeight:所有权重大于 0 的子集群的权重之和;single/avail:当仅有一个可用子集群时的快速路径优化;retryMax/crossRetry:集群内重试次数与跨集群重试次数。
subClusterBalance()函数(bal_gslb.go)实现了权重区间选子集群的算法:先对哈希键求哈希值并对totalWeight取模,再按权重依次减去,落入哪个子集群的权重区间就选择哪个子集群。
2.2 BLACKHOLE 黑洞子集群
每个集群都内置一个特殊的虚拟子集群BLACKHOLE:
- 分配到 BLACKHOLE 的流量会被直接丢弃;
- 其作用是在集群整体过载时吸收多余流量,防止流量打满整个集群,起到"卸压阀"的效果。
在源码中,当选中子集群类型为黑洞时,Balance()会直接返回ErrGslbBlackhole并累加state.ErrGslbBlackhole计数(bal_gslb.go),请求不会被转发到任何后端。
2.3 配置示例与权重计算
官方文档给出的典型多 IDC 场景为:
- 两个 IDC:IDC_1、IDC_2;
- 两个 BFE 集群:BFE_1、BFE_2;
- 两个后端子集群:SubCluster_1、SubCluster_2。
各 BFE 集群的权重配置形如:
- BFE_1:
{SubCluster_1: W11, SubCluster_2: W12, Blackhole: W1B} - BFE_2:
{SubCluster_1: W21, SubCluster_2: W22, Blackhole: W2B}
例如,若 BFE_1 的配置为{W11, W12, W1B} = {45, 45, 10},则流量按45%、45%、10%的比例分别流向 SubCluster_1、SubCluster_2 和 BLACKHOLE。
alt: BFE 依据子集群权重表(Forwarding Table)在多个子集群间分发流量的转发示意图
在仓库中,这类权重表以 JSON 数据文件的形式存放,例如 gslb_1.data:
{ "clusters": { "c1": { "GSLB_BLACKHOLE": 0, "c1.example.a": 0, "c1.example.b": 50, "c1.example.c": 20, "c1.example.d": 30 }, "c2": { "GSLB_BLACKHOLE": 0, "c2.example.a": 0, "c2.example.b": 100 } }, "hostname": "gslb-sch.b", "ts": "20190516151616" }可以看到:子集群权重值即配置为整数(如 50/20/30),GSLB_BLACKHOLE同样作为普通子集群出现在表中,权重为 0 表示当前不向其分配流量。配置加载逻辑见 gslb_conf_load.go。
2.4 子集群选择的哈希策略
子集群级均衡默认基于哈希完成,以保持同一特征的请求稳定落在同一子集群。HashConf定义了哈希策略(cluster_conf_load.go):
| HashStrategy 取值 | 说明 |
|---|---|
ClientIdOnly | 仅使用请求头中的 Client ID 作为哈希键 |
ClientIpOnly | 仅使用客户端 IP 作为哈希键(默认策略) |
ClientIdPreferred | 优先使用 Client ID,取不到时回退到客户端 IP |
RequestURI | 使用请求 URI 作为哈希键 |
对应实现位于BalanceGslb.getHashKey()(bal_gslb.go),其中还支持通过HashHeader指定"代表唯一客户端"的请求头字段,特殊格式Cookie:Key可指定从 Cookie 中取值(bal_gslb.go)。若最终哈希键为空,则退化为随机值,避免全部请求集中到同一子集群。
三、实例级负载均衡(Instance Level Load Balance)
3.1 两种内置调度算法
一个子集群通常由多个后端实例组成。BFE 在子集群内部提供了多种实例分发策略,文档明确列出的两种为:
- WRR(Weighted Round Robin,加权轮询);
- WLC(Weighted Least Connection,加权最少连接)。
实例可以依据其容量被赋予不同的权重。BalanceRR.Balance()通过algor参数分发到具体算法(bal_rr.go),支持的模式常量包括:
WrrSimple = 0 // 简单加权轮询 WrrSmooth = 1 // 平滑加权轮询 WrrSticky = 2 // 加权轮询 + 会话保持 WlcSimple = 3 // 简单加权最少连接 WlcSmooth = 4 // 平滑加权最少连接在集群配置中,BalanceMode取值WRR或WLC(cluster_conf_load.go),默认是WRR。Balance()中根据该模式选择WlcSmooth或WrrSmooth(bal_gslb.go)。
3.2 平滑加权轮询(Smooth WRR)原理
bal_rr.go头部注释给出了平滑 WRR 的完整算法与推演:
在每次选择后端时:① 将每个可用后端的
CurrentWeight增加其weight;② 选择CurrentWeight最大的后端,并将其CurrentWeight减去所有后端权重之和。
例如权重为{a:5, b:1, c:1}时,得到的选择序列是aabacaa,而不是简单的abcaaaa——这正是平滑算法的价值:避免权重大的后端被连续打满,使流量分布更均匀。完整推演过程见 bal_rr.go。
3.3 加权最少连接(WLC)
WLC 先筛选出connNum / weight比值最小的候选后端集合(leastConnsBalance(),bal_rr.go)。为避免浮点比较,源码使用交叉相乘connNum*bWeight - bConnNum*weight来比较(compLCWeight(),bal_rr.go)。当候选集只有一个时直接返回;多个候选时,WlcSmooth再叠加平滑轮询、WlcSimple则随机挑选,兼顾"最少连接"与"分布均匀"。
每个后端实例当前持有的连接数由BfeBackend.connNum维护,并提供ConnNum()/IncConnNum()/DecConnNum()访问接口(bfe_backend.go)。
3.4 实例权重的配置方式
实例列表与权重配置在集群后端表中定义,参见 cluster_table_1.conf:
{ "Config": { "p1": { "light.p1.dx": [ { "name": "p1.example10.a", "addr": "10.38.133.59", "port": 8060, "weight": 10 } ], "light.p1.wt": [ { "name": "p1.example0.b", "addr": "10.23.238.42", "port": 8060, "weight": 10 } ] } } }每个后端实例由name、addr、port、weight四个字段描述,权重值即实例级调度依据。此外BackendBasic还提供SlowStartTime(秒)实现慢启动:新恢复的后端在指定时间内权重逐步增加到全量值,避免刚上线的实例被瞬间打满(cluster_conf_load.go)。
四、健康检查(Health Check)
4.1 每个实例的两态状态机
BFE 为每个后端实例维护一个状态机,仅有两个状态:
- NORMAL:实例正常处理请求;
- CHECKING:实例处理请求失败,BFE 启动健康检查,直到实例恢复正常。
状态迁移条件如下:
| 迁移方向 | 触发条件 |
|---|---|
| NORMAL → CHECKING | 连接实例或向其发送请求的连续失败次数超过阈值 |
| CHECKING → NORMAL | BFE 收到该实例对健康检查请求的正确响应 |
在源码中,这两个状态由BfeBackend的avail字段体现,并伴随两个计数器:
failNum:正常请求的连续失败次数;succNum:健康检查请求的连续成功次数(bfe_backend.go)。
当后端被判定为失败时,UpdateStatus()会启动一个健康检查 goroutine(每个后端至多一个),见 health_check.go。
4.2 健康检查流程
check()是健康检查主循环(health_check.go):
- 按
CheckInterval周期执行探测; - 探测失败则重置
succNum,继续等待下一次检查; - 探测成功后累加
succNum,直到达到SuccNum阈值; - 达到阈值后调用
SetAvail(true)将实例状态置回 NORMAL,并标记SetRestart(true)触发慢启动。
CheckConnect()根据配置的协议(Schem)分派到不同探测实现(health_check.go):
http:发送 HTTP GET 请求并校验状态码(checkHTTPConnect);tcp:仅做 TCP 连接探测(checkTCPConnect);https/tls:建立 TLS 连接并完成握手,可选校验响应状态码(checkHTTPSConnect)。
4.3 健康检查配置参数
健康检查的完整可配置项在BackendCheck结构体中定义(cluster_conf_load.go):
| 配置项 | 含义 |
|---|---|
Schem | 探测协议:HTTP / HTTPS / TCP |
Uri | 健康检查请求的 URI |
Host | 探测请求使用的 Host 头 |
StatusCode | 期望的响应状态码,默认 200 |
StatusCodeRange | 状态码范围,如3xx、4xx、5xx,或用\|组合,如"503" \| "4xx" |
FailNum | 判定实例不健康的连续失败阈值 |
SuccNum | 判定实例恢复健康的连续成功阈值 |
CheckTimeout | 探测超时,单位毫秒 |
CheckInterval | 探测间隔,单位毫秒 |
对应配置文件示例(cluster_conf_1.conf):
"CheckConf": { "Uri": "/health", "FailNum": 3, "CheckInterval": 1000, "Response": "200 OK" }注意该示例中的Response为旧字段;现代配置中建议使用StatusCode/StatusCodeRange表达期望响应。健康检查配置是按集群独立存储的,通过SetCheckConfFetcher注入获取回调(health_check.go)。
五、自动重试(Automatic Retries)
当请求转发失败时,BFE 支持两种重试方式:
- In-Sub-Cluster Retry(子集群内重试):在同一个子集群内重新转发请求;
- Cross-Sub-Cluster Retry(跨子集群重试):转发失败后,改向其他子集群重试。
重试次数由GslbBasicConf控制(cluster_conf_load.go):
| 配置项 | 含义 | 源码默认值 |
|---|---|---|
RetryMax | 在指定子集群内的最大重试次数 | DefaultRetryMax = 3 |
CrossRetry | 跨子集群的最大重试次数 | DefaultCrossRetryMax = 1 |
对应常量定义见 bal_gslb.go。配置示例(cluster_conf_1.conf):
"GslbBasic": { "CrossRetry": 1, "RetryMax": 3 }在Balance()的实现中(bal_gslb.go):
- 当
req.RetryTime > retryMax + crossRetry时,判定重试次数耗尽,返回ErrBkRetryTooMany; - 在
retryMax范围内优先在当前子集群内选后端; - 若当前子集群所有后端均不可用,且
crossRetry > 0,则调用randomSelectExclude()(bal_gslb.go)随机挑选一个排除当前子集群与黑洞的子集群进行跨集群转发; - 若
crossRetry <= 0,则禁用跨集群重试,直接返回ErrBkNoBackend。
六、连接池(Connection Pool)
BFE 与后端实例之间支持两种 TCP 连接方式:
- 短连接(Short-Lived Connection):每个请求都新建 TCP 连接转发;
- 连接池(Connection Pool):复用已有连接。
连接池的工作流程(对应 bal_gslb.go 所依赖的 backend 连接管理):
- BFE 为每个后端实例维护一个连接池;
- 请求转发到某实例时:若池中有空闲连接则取出复用,否则新建 TCP 连接;
- 请求处理完毕后:若池中空闲连接数小于配置容量,则将该连接回收入池;否则直接关闭。
连接池容量由BackendBasic中的两个参数控制(cluster_conf_load.go):
MaxIdleConnsPerHost:每个后端实例允许保留的最大空闲连接数(即"配置容量");MaxConnsPerHost:每个后端实例允许的最大连接总数(0 表示不限制)。
这两个参数在配置加载时均有默认值兜底(cluster_conf_load.go)。合理设置MaxIdleConnsPerHost可在"连接复用收益"与"空闲连接占用"之间取得平衡。
七、会话保持(Session Stickiness)
7.1 会话的标识方式
BFE 支持会话保持,即同一会话的请求被固定转发到同一后端。会话可以基于以下请求信息定义:
- 源 IP(Source IP);
- 请求头中的字段、Cookie 等(如
Cookie:BAIDUID)。
7.2 两个粒度的会话保持
文档明确区分了两个支持层级:
- 子集群级(Sub Cluster Level):同一会话的请求被转发到同一个子集群(子集群内可以是不同实例)。实现上对应
HashConf.SessionSticky配置:开启后,实例级算法切换为WrrSticky,结合哈希键将同一会话的请求稳定映射到同一子集群(bal_gslb.go)。 - 实例级(Instance Level):同一会话的请求被转发到同一个实例。源码通过
LookupStickyBackend()在请求上下文中查找之前绑定的后端(bal_gslb.go),对应的mod_session_sticky模块(mod_session_sticky)负责生成并维护会话与后端的绑定关系。
配置层面,会话保持相关字段集中在HashConf(cluster_conf_load.go):
| 配置项 | 含义 |
|---|---|
HashStrategy | 哈希策略(决定会话键来源) |
HashHeader | 代表唯一客户端的请求头,支持Cookie:Key格式 |
SessionSticky | 是否开启会话保持(布尔值,默认关闭) |
开启会话保持后,stickyBalance()会基于哈希键与各可用后端的权重,将同一会话稳定映射到同一后端(bal_rr.go)。
八、小结
BFE 的流量均衡能力可以概括为一张"两级调度 + 四类保障"的完整体系:
| 层级/机制 | 核心实现 | 关键配置 |
|---|---|---|
| 子集群级均衡 | bal_gslb.go | 子集群权重表、HashStrategy、SessionSticky |
| 实例级均衡 | bal_rr.go | BalanceMode(WRR/WLC)、实例weight、SlowStartTime |
| 健康检查 | health_check.go | FailNum、SuccNum、CheckInterval、Schem |
| 自动重试 | bal_gslb.go | RetryMax、CrossRetry |
| 连接池 | backend 连接管理 | MaxIdleConnsPerHost、MaxConnsPerHost |
| 会话保持 | mod_session_sticky | SessionSticky、HashHeader |
实际部署时,你可以参考仓库中的真实配置样例快速起步:gslb 权重表样例、集群基础配置样例、后端实例表样例。三者分别对应子集群权重、集群级参数(重试/健康检查/超时)与实例列表,组合起来即构成一次完整的两级流量均衡配置。
- 后端
- 网络/通信
- 云原生
【免费下载链接】bfe
A modern layer 7 load balancer from baidu
相关推荐
BFE 子集群负载均衡配置指南:深入解析 gslb.data 权重配置与 GSLB_BLACKHOLE 黑洞机制
BFE 子集群负载均衡配置指南:深入解析 gslb.data 权重配置与 GSLB_BLACKHOLE 黑洞机制 导读 gslb.data 是 BFE(一个百度
后端网络/通信云原生uWebSockets集群负载均衡完全指南:5步实现高性能WebSocket应用会话保持
uWebSockets集群负载均衡完全指南:5步实现高性能WebSocket应用会话保持 在现代实时应用中,WebSocket技术已经成为连接客户端与服务器的关
后端网络消息路由WebSocketApache SkyWalking OAP 集群负载均衡实战:SkyWalking Satellite 与 EnvoyFilter 连接限流
Apache SkyWalking OAP 集群负载均衡实战:SkyWalking Satellite 与 EnvoyFilter 连接限流 SkyWalkin
可观测性后端微服务云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考