知识卡片

用专用模块直接对接服务发现,绕开"改配置就要reload"的固有代价

普通读书笔记卡

内容

Nginx要感知后端服务实例的动态变化(服务上下线、扩缩容),开源社区常见的方案是用Consul Template监听服务变化、生成新的Nginx配置文件,再触发Nginx reload让新配置生效——但Nginx的reload即使是graceful(优雅)的,在真实高QPS压力场景下,reload会引发大量连接需要重新建立,这个重连过程本身会产生相当数量的失败请求,reload操作看似”平滑”,代价却直接体现在了失败率上。微博团队没有沿用这条”改配置+reload”的路径,而是直接开发了一个用C语言实现的Nginx-upsync-module,让Nginx的upstream配置能够直接声明要对接的Consul集群,由这个模块在运行时动态地从Consul同步后端服务列表的变化,完全不需要重新加载配置文件、不需要重启Nginx worker进程——这个模块已经在线上验证,20万QPS压力下没有出现问题,并且开源在GitHub上供社区使用。这个案例给出了一条排查性能瓶颈根因的重要思路:当一个方案的”代价”看似来自某个操作本身(这里最初可能被归咎为”reload不够优雅”),但深挖后发现真正的代价来源其实是这个操作背后必然引发的连锁反应(reload导致大量连接被迫重建),解决问题的关键就不是去优化这个操作本身(比如试图让reload更快),而是要审视有没有办法从架构上彻底绕开这个操作——不再依赖”生成配置文件+触发reload”这条固有路径,而是让目标组件(Nginx)具备直接、动态感知外部状态变化的能力,从根源上消除引发连锁反应的那个动作。

参考来源

- 位置:《高可用架构(第1卷)》第4章《容器与云计算》"4.1 微博基于Docker容器的混合云迁移实战"节,"4.1.3 容器的编排与服务发现"(源文件:_epub-src/OEBPS/Text/Chapter4_1_4.xhtml) - 结论依据:原文说明"开源社区的方案例如Consul Template,都需要进行reload操作,而reload过程中会造成大量的失败请求。我们现在基于Consul实现了一个Nginx-upsync-module……Nginx仅需在upstream配置中声明Consul集群,即可完成后端服务的动态发现……目前验证在20万QPS压力没有问题",直接支撑本卡片结论。 - 原始内容:开源社区的方案例如Consul Template,都需要进行reload操作,而reload过程中会造成大量的失败请求。我们现在基于Consul实现了一个Nginx-upsync-module……Nginx仅需在upstream配置中声明Consul集群,即可完成后端服务的动态发现……目前验证在20万QPS压力没有问题。