http api 除了操作简单、方便,而且实时性好;缺点是有多台 Nginx 时,不同 Nginx 路由表的一致性难于保证,如果某一条注册失败,便会造成服务配置的不一致,容错复杂。另外扩容 Nginx 服务器,需要从其他的 Nginx 中同步路由表。 pull 方案
考虑到 push 方案中路由表维度中存在的一致性待问题,引入了第三方组件 consul 解决这一问题。
架构图如下:
路由表中所有的 backend 信息( 含 meta )存储到 consul ,所有的 Nginx 从 consul 拉取相关信息,有变更则更新路由表,利用 consul 解决一致性问题,同时利用 consul 的 wait 机制解决实时性问题,利用 consul 的 index ( 版本号 ) 进行增量摘取,解决带宽占用问题。
在 consul 中,一个 k/v 对代表一个 backend 信息,增加一个即视作扩容,减少一个即为缩容。调整 meta 信息,如权重,也可以达到动态流量调整的目的。
下面的实现基于 consul 进行介绍。
基于动态路由的方案实现
基于 upsync 方式,开发了模块 nginx-upsync-module,它的功能是拉取 consul 的后端 server 的列表,并更新 Nginx 的路由信息。此模块不依赖于任何第三方模块。 路由表更新方式
consul 作为 Nginx 的 db,利用 consul 的 KV 服务,每个 Nginx work 进程独立的去拉取各个 upstream 的配置,并更新各自的路由。
流程图如下:
每个 work 进程定时的去 consul 拉取相应 upstream 的配置,定时的间隔可配;其中 consul 提供了 time_wait 机制,利用 value 的版本号,若 consul 发现对应 upstream 的值没有变化,便会 hang 住这个请求 5 分钟(默认),在这五分钟内对此 upstream 的任何操作,都会立刻返回给 Nginx,对相应路由进行更新。对于拉取的间隔可以结合场景的需要进行配置,基本可以实现所要求的实时性。upstream 变更后,除了更新 Nginx 的缓存路由信息,还会把本 upstream 的后端 server 列表 dump 到本地,保持本地 server 信息与 consul 的一致性。
除了注册/注销后端的 server 到 consul,会更新到 Nginx 的 upstream 路由信息外,对后端 server 属性的修改也会同步到nginx的upstream路由。当前本模块支持修改的属性有 weight、max_fails、fail_timeout、down,修改 server 的权重可以动态的调整后端的流量,若想要临时移除server,可以把 server 的 down 属性置为 1( 当前 down 的属性暂不支持 dump 到本地的 server 列表内 ),流量便会停止打到该 server,若要恢复流量,可重新把 down 置为 0。
另外每个 work 进程各自拉取、更新各自的路由表,采用这种方式的原因:一是基于 Nginx 的进程模型,彼此间数据独立、互不干扰;二是若采用共享内存,需要提前预分配,灵活性可能受限制,而且还需要读写锁,对性能可能存在潜在的影响;三是若采用共享内存,进程间协调去拉取配置,会增加它的复杂性,拉取的稳定性也会受到影响。基于这些原因,便采用了各自拉取的方式。 高可用性
Nginx 的后端列表更新依赖于 consul,但是不强依赖于它,表现在:一是即使中途consul意外挂了,也不会影响 Nginx 的服务,Nginx 会沿用最后一次更新的服务列表继续提供服务;二是若 consul 重新启动提供服务,这个时候 Nginx 会继续去 consul 探测,这个时候 consul 的后端服务列表发生了变化,也会及时的更新到 Nginx。
另一方面,work 进程每次更新都会把后端列表 dump 到本地,目的是降低对 consul 的依赖性,即使在 consul 不可用之时,也可以 reload Nginx。Nginx 启动流程图如下:
Nginx 启动时,master 进程首先会解析本地的配置文件,解析完成功,接着进行一系列的初始化,之后便会开始 work 进程的初始化。work 初始化时会去 consul 拉取配置,进行 work 进程 upstream 路由信息的更新,若拉取成功,便直接更新,若拉取失败,便会打开配置的 dump 后端列表的文件,提取之前 dump 下来的 server 信息,进行 upstream 路由的更新,之后便开始正常的提供服务。
每次去拉取 consul 都会设置连接超时,由于 consul 在无更新的情况下默认会 hang 五分钟,所以响应超时配置时间应大于五分钟。大于五分钟之后,consul 依旧没有返回,便直接做超时处理。 兼容性
整体上讲本模块只是更新后端的 upstream 路由信息,不嵌入其它模块,同时也不影响其它模块的功能,亦不会影响 Nginx-1.9.9 的几种负载均衡算法:least_conn、hash_ip 等。
除此之外,模块天然支持健康监测模块,若 Nginx 编译时包含了监测模块,会同时调用健康监测模块的接口,时时更新健康监测模块的路由表。 性能测试
nginx-upsync-module 模块,潜在的带来额外的性能开销,比如间隔性的向 consul 发送请求,由于间隔比较久,且每个请求相当于 Nginx 的一个客户端请求,所以影响有限。基于此,在相同的硬件环境下,使用此模块和不使用此模块简单做了性能对比。