项目简介
BFE是一个开源的七层负载均衡系统,和Nginx功能类似,但是比Nginx多了集群负载均衡功能、转发规则使用的是条件表达式(类似于表达式求值功能,Nginx使用的是正则表达式),提供了丰富的内部状态展示统计信息等。详情
功能组件
BFE包含了多个组件,部分是可选的:
BFE Server: BFE数据平面核心转发模块(必选,已开源)
BFE-Reader: BFE日志分析模块,与BFE Server部署在一起,用于对BFE日志进行本地汇聚计算,降低后续计算处理的数据规模。
BFE-API Server: BFE控制平面API Server,其它控制平面模块以BFE-API Server为核心协同工作。
BFE-Aggregator: BFE日志数据实时聚合计算模块。
BFE-Scheduler: BFE流量调度器,基于流量、容量、网络距离及质量,自动计算全局负载均衡(GSLB)策略。
BFE-Controller: BFE集群控制器,执行常规控制任务,例如异常巡检及报警。
BFE-Web UI: BFE Web控制台。
其它周边依赖系统, 例如缓存服务、非对称密码学算法计算服务等。
在上述组件中,只有 BFE-Server开源了,其他都尚未开源。
概念说明
产品线:Product,也被成为“租户”。BFE的配置,比如转发策略、权限等,是已产品线为单位来进行设置
集 群:Cluster,把具有同类功能的后端集合定义成为一个集群
子集群:Subcluster,集群又可细分成多个子集群,通常把处于同一个IDC的后端定义为子集群
实 例:Instance,每个子集群可包含多个后端服务实例,每个后端实例通过“IP地址+端口号”标识
比如:外卖产品线有多个服务集群(用户端、商家端、管理端等),其中用户端集群有N个子集群(DX,YF集群),每个子集群里面有多个服务实例
流量接入转发说明
Step 1-2:DNS解析
- 请求的域名为 demo.example.com
- 返回IP地址为 6.6.6.6(示例地址)
Step 3:用户与 6.6.6.6:80 建立TCP连接并发送HTTP请求,IP报文被路由到IDC1的入口,由四层负载均衡设施处理
Step 4:四层负载均衡设施将报文转发给下游BFE
Step 5:BFE收到HTTP请求, 确定处理该请求的产品线
- BFE根据HTTP请求头中的Host字段, 确定产品线
- 对于demo.example.com域名,假设对应的产品线名为demo
Step 6:BFE根据产品线的分流规则,选择该请求的目的集群
- 对于这个请求,假设对应的目的集群为demo-static
- 详见基于内容路由说明
Step 7-8:BFE根据产品线的均衡策略,选择子集群及实例
- 对于这个请求,假设子集群为demo-static.idc1,实例为demo-static-01.idc1
- 详见流量负载均衡说明
Step 9:请求被发往后端实例demo-static-01.idc1
Step 10:BFE收到后端实例回复的响应
Step 11-12:BFE通过四层负载均衡设施,将响应返回给用户
路由规则
根据流量接入转发模型中的流程,服务路由流程如下:
- BFE根据HTTP请求头中的Host字段, 确定产品线
- 根据产品线的分流规则,选择该请求的目的集群
- 根据均衡策略,把请求转到具体的某个 子集群及实例 并把处理后的结果返回
示例
用户请求 demo.example.com,BFE根据HTTP请求中的Host字段,确定产品线为:demo
- 产品线demo,包含以下3种服务集群:
- 静态集群(demo-static):服务静态流量
- post集群(demo-post):服务post流量
- main集群(demo-main):服务其他流量
- 期望的转发条件如下:
- 对于path以"/static"为前缀的,都发往demo-static集群
- 请求方法为"POST"、且path以"/setting"为前缀的,都发往demo-post
- 其它请求,都发往demo-main
- 对应以上要求,产品线demo的转发表如下图所示
负载均衡规则
如何把流量均衡分配到集群中的多个子集群及其内部的实例?这个需求在多IDC场景中非常常见。 详情
另外,为了防止集群整体过载,也可以使用虚拟黑洞集群来主动丢弃(Blackhole)。
实现策略:
- 设置子集群权重(分流比例),BFE根据配置设做加权轮询调度(WRR),把服务指向子集群;
- 通一子集群内的多个实例之间,支持多种负载均衡算法:WRR加权轮询、WLC加权最小连接数,支持根据实例处理能力不同设置不同的权重。
健康检测:BFE为每个服务实例维护一个状态机,正常状态或不可用状态,对不可用状态亦会不断进行检测直至恢复可用。
失败重试机制:支持同子集群重试、跨子集群重试
连接池:支持短连接、连接池方式
会话保持:支持将相同来源请求转发至固定的业务后端(某个子集群或某个实例)
Quick Start
配置组织
BFE的核心配置是bfe.conf (conf/bfe.conf),为便于维护, 配置按功能分类存放在相应目录 conf/
功能类别
文件位置
描述
服务基础配置
conf/bfe.conf
定义服务监听端口、超时时间、模块、session等
接入协议配置
conf/tls_conf/ 目录
密钥,协议支持等
流量路由配置
conf/server_data_conf/ 目录
host_rule.data:产品线域名表配置文件
vip规则、路由规则等
负载均衡配置
conf/cluster_conf/ 目录
扩展模块配置
conf/mod_
说明:
1.产品线域名表配置文件 : conf / server_data_conf / host_rule.data
目的:实现企业多域名都指向同一product的需求,比如: abc.com / abc.cn 都由 productX 提供服务。
{
"Version": "init version",
"DefaultProduct": null,
"Hosts": { //域名标签和域名列表的映射关系表
"exampleTag1":[ //域名标签
"example1.org" //域名列表
"example2.org"
],
"exampleTag2":[
"xxx.org"
"zzz.org"
]
},
"HostTags": { //产品线和域名标签的映射关系
"example_product1":[ //产品线名称
"exampleTag1" //域名标签列表
],
"product2":[
"exampleTag2"
]
}
}
2.VIP规则配置文件 : conf / server_data_conf / vip_rule.data
目的:vip_rule和host_rule类似,host_rule是通过域名路由到product;vip_rule通过请求来源的IP路由到product,vip_rule在host_rule失败时生效。在百度内部,BFE的前面是四层负载均衡,每个产品线一般都有固定的vip,所以vip也可以起到像域名一样的路由作用。
{
"Version": "init version",
"Vips": { //各产品线的VIP列表
"example_product": [ //产品线名称
"111.111.111.111" //VIP列表
]
}
}
3.路由分流配置文件:conf / server_data_conf / router_rule.data
目的:根据规则把请求路由到具体的集群
{
"Version": "init version",
"ProductRule": { //各产品线的分流规则配置
"example_product": [ //产品线名称
{
"Cond": "req_host_in(\"example.org\")", //分流条件, 语法详见Condition
"ClusterName": "cluster_example" //目的集群
},
{
"Cond": "default_t()",
"ClusterName": "cluster_example"
}
]
}
}
4.集群转发规则配置文件:conf / server_data_conf / cluster_conf.data
目的:根据规则把请求路由到具体的集群
{
"Version": "init version",
"Config": {
"cluster_example": { //集群名称
"BackendConf": { //后端基础配置
"TimeoutConnSrv": 2000, //连接后端的超时时间,单位是毫秒,默认值2
"TimeoutResponseHeader": 50000, //从后端读响应头的超时时间,单位是毫秒,默认值60
"MaxIdleConnsPerHost": 0, //BFE实例与每个后端的最大空闲长连接数,默认值2
"RetryLevel": 0 //请求重试级别。0:连接后端失败时,进行重试;1:连接后端失败、转发GET请求失败时均进行重试,默认值0
},
"CheckConf": { //健康检查配置
"Schem": "http", //健康检查协议,支持HTTP和TCP,默认值 HTTP
"Uri": "/healthcheck",//健康检查请求URI (仅HTTP),默认值 /health_check
"Host": "example.org", //健康检查请求HOST (仅HTTP),默认值 ""
"StatusCode": 200, //期待返回的响应状态码 (仅HTTP),默认值 0,代表任意状态码
"FailNum": 10, //健康检查启动阈值(转发请求连续失败FailNum次后,将后端实例置为不可用状态,并启动健康检查),默认值5
"SuccNum": 5, //健康检查成功阈值(健康检查连续成功SuccNum次后,将后端实例置为可用状态),默认值1
"CheckTimeout":500,//健康检查的超时时间,单位是毫秒,默认值0(无超时)
"CheckInterval": 1000 //健康检查的间隔时间,单位是毫秒,默认值1
},
"GslbBasic": { //GSLB基础配置
"CrossRetry": 0, //跨子集群最大重试次数,默认值0
"RetryMax": 2, //子集群内最大重试次,默认值2
"BalanceMode":"WRR", //负载均衡模式(WRR: 加权轮询; WLC: 加权最小连接数),默认值WRR
"HashConf": { //会话保持的HASH策略配置
"HashStrategy": 0, //会话保持的哈希策(ClientIdOnly, ClientIpOnly, ClientIdPreferred),默认值ClientIpOnly
"HashHeader": "Cookie:UID", //会话保持的hash请求头
"SessionSticky": false, //是否开启会话保持(开启后,可以保证来源于同一个用户的请求可以发送到同一个后端),默认值false
}
},
"ClusterBasic": { //集群基础配置
"TimeoutReadClient": 30000, //读用户请求wody的超时时间,单位为毫秒,默认值30
"TimeoutWriteClient": 60000, //写响应的超时时间,单位为毫秒,默认值60
"TimeoutReadClientAgain": 30000,//连接闲置超时时间,单位为毫秒,默认值60
"ReqWriteBufferSize": 512, //Write buffer size for request in byte
"ReqFlushInterval": 0,//Interval to flush request in ms. if zero, disable periodic flush
"ResFlushInterval": -1,//Interval to flush response in ms. if zero, disable periodic flush
"CancelOnClientClose": false //Cancel blocking operation on server if client connection disconnected
}
}
}
}
5.负载均衡
- 子集群负载均衡则配置文件:conf / cluster_conf / gslb.data
目的:各集群集群内的多个子集群直接分流比例(GSLB)
{
"Hostname": "", //配置文件生成来源信息
"Ts": "0", //配置文件生成的时间戳
"Clusters": { //各集群中子集群的分流权重
"cluster_example": { //集群名称
"GSLB_BLACKHOLE": 0, //GSLB_BLACKHOLE(保留,黑洞子集群),分配到该子集群的流量将被丢弃,用于过载保护, 0代表权重(不参与流量任务),子集群分流权重之和应等于 100
"example.bfe.bj": 100 // 子集群名 -> 子集群承接流量的权重
}
}
}
- 实例负载均衡则配置文件:conf / cluster_conf / cluster_table.data
目的:记录各后端集群包含的子集群及实例
{
"Version": "init version",
"Config": { //各集群信息配置
"cluster_example": { //集群名称
"example.bfe.bj": [ //子集群名称
{ //子集群配置信息,包含多个实例配置
"Addr": "10.199.189.26", //实例监听地址
"Name": "example_hostname", //机器名称
"Port": 8181, //实例监听端口
"Weight": 10 //实例名称
}
]
}
}
}
6.名字规则配置文件:conf / server_data_conf / name_conf.data
目的:记录服务名字和服务实例的映射关系。BFE很多配置用的是bns(一种名字服务,一个名字可以解析为一组服务实例的ip和端口,权重)。name.conf可以配置本地的名字
{
"Version": "init version",
"Config": { //名字和实例的映射关系
"example.redis.cluster": [ //集群名称
{ //实例信息
"Host": "192.168.1.1", //实例地址
"Port": 6439, //实例端口
"Weight": 10 //实例权重
}
]
}
}