Nginx是一个被广泛使用的反向代理(Reverse Proxy)开源软件。在“反向代理”这个方向上,BFE是被设计用来替代Nginx的。于是,一个被经常提出的问题出现了:为什么要使用BFE?和Nginx相比,BFE到底有什么地方是不同的。
BFE和Nginx最大的不同是设计出发点和转发模型。BFE从一开始就是为转发场景设计的,能够很好的满足各种转发场景的需求;而Nginx本来是作为Web Server设计的,用来做转发是“半路出家”,在模型方面存在一些问题。
本文将重点说明两者在“转发模型”上的差异。
1. 对Nginx的分析
1.1 Nginx的转发配置
首先看看Nginx是怎么做的。
Nginx配置中的核心概念是server, location,upstream。
- server:代表一个web server,可以定义web server的监听端口和主机名
- location:代表一个web server之下的各路径/接口
- upstream:代表被转发的目标
一个典型的Nginx转发配置如下所示:
# 定义转发目标集群cluster_a
upstream cluster_a {
server 192.168.1.1:8080;
server 192.168.1.2:8080;
}
# 定义转发目标集群cluster_b
upstream cluster_b {
server 192.168.1.3:8080;
server 192.168.1.4:8080;
}
# 定义www.a.com的转发规则
server {
listen 80;
server_name www.a.com; # host
location / { # path, 设置为any
proxy_pass http://cluster_a;# 转发给cluste_a
}
}
# 定义www.b.com的转发规则
server {
listen 80;
server_name www.b.com; # host
location / { # path,设置为any
proxy_pass http://cluster_b; # 转发给cluster_b
}
}
如果要在转发过程中做一些特殊的处理,则在以上的配置中插入相关的语句。例如,针对www.a.com的请求,可以增加如下rewrite规则:
server {
listen 80;
server_name www.a.com;
location / {
rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg)$' /data?file=$3.$4;
proxy_pass http://cluster_a
}
}
1.2 Nginx转发模型的问题
虽然目前很多人将Nginx用于转发场景,但是不得不说,Nginx的转发机制有比较严重的问题。
(1) 转发配置的描述能力较弱
对于一个转发场景来说,配置规则的基本逻辑是:转发条件 => 转发目标
多条转发规则可以形成一张转发表。
转发条件 | 转发目标 |
---|---|
Host=www.a.com | cluster_a |
Host=www.b.com | cluster_b |
在Nginx中,并没有出现明确的转发表概念,转发规则的描述受到配置中server-location结构的限制。server-location结构本来是设计用于web server的,用于转发就比较笨重。例子中这种简单的转发逻辑需要比较多的配置语句。
在Nginx开源版本中,location描述机制在表达能力方面的缺点具体表现在:
- 仅支持基于uri的条件,无法直接支持基于请求Header或请求上下文的条件
- 针对常见的uri包含匹配、后缀匹配也不得不使用正则表达式
- 难以对多个条件灵活进行逻辑组合(与或非)
(2) 转发规则的优先级较复杂
在存在多条转发规则的时候,优先级的控制就变得非常重要。
Nginx在匹配优先级上做了一定的限制,必须首先匹配Host以确定server,然后在server内匹配location。这个限制在简单的场景下没有问题,但是在比较复杂的场景下可能会制约规则的编写。
在匹配server和匹配location时,都有比较复杂的规则(见下)。在比较复杂的场景下,最终生效的结果并不容易预测。
匹配server时,规则如下:
1、完全匹配
2、通配符在前的,如*.test.com
3、在后的,如www.test.*
4、正则匹配,如~^\.www\.test\.com$
如果都不匹配
1、优先选择listen配置项后有default或default_server的
2、找到匹配listen端口的第一个server块
在匹配location时,规则如下:
1. 字符串最长匹配(非正则表达式)
和在配置文件中出现的先后顺序无关
2. 正则匹配
正则表达式按照配置文件中顺序匹配,一旦匹配,不会继续匹配
(3) 大量使用正则表达式
在Nginx中,对于稍微复杂的场景,就需要在转发规则的描述中使用正则表达式。
在实践中,我们发现正则表达式存在以下两个严重问题:
- 配置难以维护。正则表达式存在严重的可读性问题。用正则表达式编写的转发条件很难看懂,且容易存在二义性。
- 性能存在隐患。对于编写不当的正则表达式,可能在特定的流量特征下会出现严重的性能退化。
(4) “基础转发”和“转发的附加处理”混杂在一起
在反向代理的转发模型中,需要容纳以下两个基本功能:
- 基础转发配置。定义转发的条件和转发的目标。
- 转发的附加处理。定义在转发中需要做的操作,如rewrite, redirect等等。
在Nginx中,“基础转发”和“转发的附加处理”(如,rewrite, redirect等)只能混在一起写。在一些情况下,“转发的附加处理”和“基础转发”的匹配逻辑可能并不完全一致,在Nginx中配置维护比较困难。
(5) 不支持在多组实例间按照比例调度
在多数据中心或多容器云的场景下,可能有这样的需求:
- 多组实例都提供相同的服务
- 在多组实例间,使用权重比例来调度流量
这个需求转换为Nginx的概念,就是需要支持对满足某种转发规则的请求,按照设定的权重转发给多个upstream。Nginx不支持这样的功能。
2. BFE和Nginx的差异
BFE的定位是“为企业级场景设计的现代七层负载均衡开源软件”。和Nginx相比,BFE有以下的变化:
(1) 面向转发场景设计
- 在BFE中明确引入了“转发表”的概念,可以清晰而简洁的设定转发条件和转发目标。
- 在BFE中,除了Host和Path外,也可以很容易的使用请求中更多的信息作为转发条件。
- 在BFE“转发表”的多条规则间,有较简单的优先级策略,更易于理解
(2) 尽量避免正则表达式的使用
针对正则表达式所存在的问题,BFE中设计了“条件表达式”(Condition Expression)机制,以提高转发规则的可维护性,并降低性能退化的隐患。
(3) 将“转发的附加处理”和“基础转发”的配置分离
如rewrite、redirect这样的处理,各自有独立的配置。
(4) 提供了多组实例间按照比例调度的功能
这个功能对于多数据中心或多容器云场景的调度非常有用。
3. BFE转发中的基本概念
在BFE中,有以下基本概念:
(1) 租户(Tenant)。使用BFE转发的业务可以基于“租户”的单位来区分。BFE引擎中的配置,比如转发策略、各扩展模块的配置等,都是以租户为单位来区分的。
在BFE中,租户也被称为“产品线(Product)”。
(2) 后端集群(Cluster)。具有同类功能的后端被定义为一个集群。对于一个租户,可以定义多个集群。在某些场景中,集群也被称为服务(Service)。在一个租户内,可以使用租户的路由转发表将流量转发给合适的集群。
(3) 后端子集群(Sub Cluster)。在多数据中心场景下,集群可以划分为多个子集群。通常,可以将集群中处于同一数据中心的后端定义为一个子集群。在某些场景中,子集群也被称为实例组(Instance Group)。子集群概念的引入,主要是为了处理多数据中心场景下的流量调度。
(4) 后端实例(Instance)。每个子集群可包含多个后端服务实例(Instance),每个后端实例通过"IP地址 + 端口号"标识。
下图中用一个例子对以上概念之间的关系做出了说明,其中包含2个租户。租户1配置了2个集群(集群A和集群B),这2个集群分别有2个子集群和1个子集群,各子集群有1~3个实例;租户2只配置了一个集群(集群C),集群C有2个子集群,2个子集群各有2个实例。
4. BFE的转发过程
下面使用一个例子来说BFE的转发流程。例子的场景如下图所示。客户端首先经过DNS解析,获得目的IP地址(步骤1-2)。之后请求首先被发送到四层负载均衡(步骤3),然后再转发给BFE(步骤4)。
在HTTP请求到达BFE后,BFE的处理步骤如下:
- 步骤 5:确定HTTP请求所属的租户。BFE可以根据HTTP请求头中的“Host”字段或HTTP请求的目标IP地址来确定租户。在本案例中,针对HTTP请求头中demo.example.com域名,BFE找到对应的租户为demo。
- 步骤 6:根据租户的分流规则,决定HTTP请求的目的集群。对于每个租户,可以配置一张独立的路由转发表。通过查找路由转发表,确定请求所属的目的集群。路由转发机制的详情将在后面的章节中介绍。在本案例中,通过查表确定对应的目的集群为demo-static。
- 步骤 7:根据集群的内网流量调度策略,选择合适的子集群。对于每个BFE集群,可以针对每个集群的各子集群设置转发权重。BFE根据设置的转发权重来执行转发操作。内网流量调度机制的详情将在后面的章节中介绍。在本案例中,假设在IDC1的BFE集群上,demo-static的3个子集群对应的转发权重为(100, 0, 0),所以,确定转发的目标子集群为demo-static.idc1。
- 步骤 8:根据集群的子集群负载均衡策略,选择合适的实例。对于每个集群,可以设置子集群的负载均衡策略,如WRR(Weighted Round Robin,加权轮询)、WLC(Weighted Least Connections,加权最小连接数)等。BFE根据子集群的负载均衡策略,在子集群中选择合适服务实例来处理请求。在本案例中,最终选择demo-static-01.idc1来处理请求。
5. BFE的转发表
在BFE内对每个租户维护一张独立的“转发表”。对于每个属于该租户的请求,通过查询转发表获得目标集群。
转发表由多条“转发规则”组成。在查询时,对多条转发规则以顺序的方式查找;只要命中任何一条转发规则,就会结束退出,其中最后一条规则为“默认规则(Default)”。在所有转发规则都没有命中的时候,执行默认规则。
每条转发规则包含两部分:匹配条件和目标集群。其中匹配条件使用BFE自研的“条件表达式“来表述。
下面展示了一个转发表的例子。在这个例子中,包含以下3种服务集群。
(1) 静态集群(demo-static):服务静态流量。
(2) post集群(demo-post):服务post流量。
(3) main集群(demo-main):服务其他流量。
期望的转发逻辑如下:
(1) 对于Path以"/static"为前缀的,都发往demo-static集群。
(2) 请求方法为"POST"且Path以"/setting"为前缀的,都发往demo-post集群。
(3) 其他请求,都发往demo-main集群。
6. 附加处理的独立配置
在BFE中,为各扩展处理模块建立了独立的配置文件,这样可以降低配置维护的复杂性,避免配置之间的耦合。
以redirect功能为例,在目录conf/mod_redirect/下,可以看到mod_redirect.conf和redirect.data这两个配置文件。
rewrite.data包含重定向规则,可动态加载。在安装包中,示例中的配置文件如下:
{
"Version": "1",
"Config": {
"example_product": [
{
"Cond": "req_path_prefix_in(\"/redirect\", false)",
"Actions": [
{
"Cmd": "URL_SET",
"Params": ["https://example.org"]
}
],
"Status": 301
}
]
}
}
7. 总结
本文从“转发模型”的角度对Nginx和BFE进行了对比。
Nginx从Web Server出发,被“借用“于反向代理场景,在转发模型方面存在多处问题。在简单的应用场景下,这些问题可能还不明显;但是在较复杂和较大规模的场景下,这些问题会显现出来。
BFE 定位于“为企业级场景设计的现代七层负载均衡开源软件”,在设计中考虑了复杂业务场景的需求,可以支持复杂的转发规则,支持多数据中心和多容器云调度场景。
由于篇幅所限,关于BFE转发模型的介绍比较粗略。有兴趣的读者可进一步查看《深入理解BFE》中的说明。此书已由电子工业出版社正式出版,书名为《万亿级流量转发 - BFE核心技术与实现》。
可通过扫描下方的二维码优惠购买。
欢迎关注“BFE开源项目”公众号,获得本项目的更多更新。谢谢!