作者 刘维
这段时间在 Reddit 看到一个探讨,为什么 NGINX 不反对热加载?乍看之下很反常识,作为世界第一大 Web 服务器,不反对热加载?难道大家都在应用的 nginx -s reload
命令都用错了?带着这个疑难,让咱们开始这次探索之旅,一起聊聊热加载和 NGINX 的故事。
NGINX 相干介绍
NGINX 是一个跨平台的开源 Web 服务器,应用 C 语言开发。据统计,全世界流量最高的前 1000 名网站中,有超过 40% 的网站都在应用 NGINX 解决海量申请。
NGINX 有什么劣势,导致它从泛滥的 Web 服务器中怀才不遇,并始终放弃高使用量呢?
我感觉外围起因在于,NGINX 天生长于解决高并发,能在高并发申请的同时放弃高效的服务。相比于同时代的其余竞争对手例如 Apache、Tomcat 等,其当先的事件驱动型设计和全异步的网络 I/O 解决机制,以及极致的内存调配治理等泛滥优良设计,将服务器硬件资源压缩到了极致。使得 NGINX 成为高性能 Web 服务器的代名词。
当然,除此之外还有一些其余起因,比方:
- 高度模块化的设计,使得 NGINX 领有无数个功能丰富的官网模块和第三方拓展模块。
- 最自在的 BSD 许可协定,使得有数开发者违心为 NGINX 奉献本人的想法。
- 反对热加载,能保障 NGINX 提供 7x24h 不间断的服务。
对于热加载
大家冀望的热加载性能是什么样的?我集体认为,首先应该是用户端无感知的,在保障用户申请失常和连接不断的状况下,实现服务端或上游的动静更新。
那什么状况下须要热加载?在现在云原生时代下,微服务架构流行,越来越多的利用场景有了更加频繁的服务变更需要。包含反向代理域名高低线、上游地址变更、IP 黑白名单更新等,这些都和热加载非亲非故。
那么 NGINX 是如何实现热加载的?
NGINX 热加载的原理
执行 nginx -s reload
热加载命令,就等同于向 NGINX 的 master 过程发送 HUP 信号。在 master 过程收到 HUP 信号后,会顺次关上新的监听端口,而后启动新的 worker 过程。
此时会存在新旧两套 worker 过程,在新的 worker 过程起来后,master 会向老的 worker 过程发送 QUIT 信号进行优雅敞开。老的 worker 过程收到 QUIT 信号后,会首先敞开监听句柄,此时新的连贯就只会流进到新的 worker 过程中,老的 worker 过程解决完以后连贯后就会完结过程。
从原理上看,NGINX 的热加载能很好地满足咱们的需要吗?答案很可能是否定的,让咱们来看下 NGINX 的热加载存在哪些问题。
NGINX 热加载的缺点
首先,NGINX 频繁热加载会造成连贯不稳固,减少失落业务的可能性。
NGINX 在执行 reload 指令时,会在旧的 worker 过程上解决曾经存在的连贯,解决完连贯上的以后申请后,会被动断开连接。此时如果客户端没解决好,就可能会失落业务,这对于客户端来说显著就不是无感知的了。
其次,在某些场景下,旧过程回收工夫长,进而影响失常业务。
比方代理 WebSocket 协定时,因为 NGINX 不解析通信帧,所以无奈晓得该申请是否为已处理完毕状态。即便 worker 过程收到来自 master 的退出指令,它也无奈立即退出,而是须要等到这些连贯出现异常、超时或者某一端被动断开后,能力失常退出。
再比方 NGINX 做 TCP 层和 UDP 层的反向代理时,它也没法晓得一个申请到底要通过多少次申请才算真正地完结。
这就导致旧 worker 过程的回收工夫特地长,尤其是在直播、新闻媒体活语音辨认等行业。旧 worker 过程的回收工夫通常能达到半小时甚至更长,这时如果再频繁 reload,将会导致 shutting down 过程继续减少,最终甚至会导致 NGINX OOM,重大影响业务。
# 始终存在旧 worker 过程:nobody 6246 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 6247 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 6247 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 6248 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 6249 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 7995 10419 0 10:30 ? 00:20:37 nginx: worker process is shutting down <= here
nobody 7995 10419 0 10:30 ? 00:20:37 nginx: worker process is shutting down
nobody 7996 10419 0 10:30 ? 00:20:37 nginx: worker process is shutting down
从上述内容能够看到,通过 nginx -s reload
形式反对的“热加载”,尽管在以往的技术场景中够用,然而在微服务和云原生迅速倒退的明天,它曾经顾此失彼且不合时宜。
如果你的业务变更频率是每周或者每天,那么 NGINX 这种 reload 还是满足你的需要的。但如果变更频率是每小时、每分钟呢?假如你有 100 个 NGINX 服务,每小时 reload 一次的话,就要 reload 2400 次;如果每分钟 reload 一次,就是 864 万次。这显然是无奈承受的。
因而,咱们须要一个不须要过程替换的 reload 计划,在现有 NGINX 过程内能够间接实现内容的更新和实时失效。
在内存中间接失效的热加载计划
在 Apache APISIX 诞生之初,就是心愿来解决 NGINX 热加载这个问题的。
Apache APISIX 是基于 NGINX + Lua 的技术栈,以 ETCD 作为配置核心实现的云原生、高性能、全动静的微服务 API 网关,提供负载平衡、动静上游、灰度公布、精细化路由、限流限速、服务降级、服务熔断、身份认证、可观测性等数百项性能。
应用 APISIX 你不须要重启服务就能够更新配置,这意味着批改上游、路由、插件时都不必重启。既然是基于 NGINX,APISIX 又是如何解脱 NGINX 的限度实现完满热更新?咱们先看下 APISIX 的架构。
通过上述架构图能够看到,之所以 APISIX 能解脱 NGINX 的限度是因为它把上游等配置全副放到 APISIX Core 和 Plugin Runtime 中动静指定。
以路由为例,NGINX 须要在配置文件内进行配置,每次更改都须要 reload 之后能力失效。而为了实现路由动静配置,Apache APISIX 在 NGINX 配置文件内配置了单个 server,这个 server 中只有一个 location。咱们把这个 location 作为主入口,所有的申请都会通过这个 location,再由 APISIX Core 动静指定具体上游。因而 Apache APISIX 的路由模块反对在运行时增减、批改和删除路由,实现了动静加载。所有的这些变动,对客户端都零感知,没有任何影响。
再来几个典型场景的形容。
比方减少某个新域名的反向代理,在 APISIX 中只需创立上游,并增加新的路由即可,整个过程中不须要 NGINX 过程有任何重启。再比方插件零碎,APISIX 能够通过 ip-restriction 插件实现 IP 黑白名单性能,这些能力的更新也是动静形式,同样不须要重启服务。借助架构内的 ETCD,配置策略以增量形式实时推送,最终让所有规定实时、动静的失效,为用户带来极致体验。
总结
NGINX 的热加载在某些场景下会长工夫存在新旧两套过程,导致额定耗费资源,同时频繁热加载也会导致小概率业务失落。面对当下云原生和微服务的技术趋势下,服务变动更加的频繁,管制 API 的策略也产生了变动,导致咱们对热加载的需要提出了新需要,NGINX 的热加载曾经不能满足理论业务需要。
当初是时候切换到更贴合云原生时代并且更欠缺的热加载策略、性能体现卓越的 API 网关——Apache APISIX,从而享受动静、对立治理等个性带来的管理效率上的极大晋升。