原副标题:为何NGINX的reload指示并非热读取?
译者 李嘉
这几天在 Reddit 看到两个探讨,为何 NGINX 不全力支持热读取?貌似很反基本常识,作为世界第一大 Web 伺服器,不全力支持热读取?何况我们都在采用的nginx -s reload指示都惹急了?带着这个疑点,让我们开始这次explore,一起谈谈热读取和 NGINX 的故事情节。
NGINX 相关如是说
NGINX 是两个虚拟化的开放源码 Web 伺服器,采用 C 词汇开发。据估计,在世界上互联网流量最高的前 1000 名中文网站中,有超过 40% 的中文网站都在采用 NGINX 处置海量数据允诺。
NGINX 有什么竞争优势,导致它从为数众多的 Web 伺服器中十强,并一直维持高采用量呢?
我觉得核心理念其原因是,NGINX 与生俱来擅于处置高mammalian,能在高mammalian允诺的同时维持高效率的服务项目。较之于同代的其它竞争者比如 Apache、Tomcat 等,其领跑的设计模式型结构设计和全触发器的互联网 I/O 处置监督机制,以及无与伦比的缓存重新分配管理等为数众多杰出结构设计,将伺服器Xen填充到了无与伦比。使 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 进程: nobody6246 6241 0 10:51 ? 00:00:00 nginx: worker process nobody6247 6241 0 10:51 ? 00:00:00 nginx: worker process nobody6247 6241 0 10:51 ? 00:00:00 nginx: worker process nobody6248 6241 0 10:51 ? 00:00:00 nginx: worker process nobody6249 6241 0 10:51 ? 00:00:00 nginx: worker process nobody7995 10419 0 10:30 ? 00:20:37 nginx: worker process is shutting down <= here nobody7995 10419 0 10:30 ? 00:20:37 nginx: worker process is shutting down nobody7996 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,从而享受静态、统一管理等特性带来的管理效率上的极大提升。
有奖问卷
马斯克晒出Twitter架构图 微软WSL 1.0发布 curl译者:不考虑其它词汇重构,计划升级C词汇标准、明年发布curl 8
这里有最新开放源码资讯、软件预览、技术干货等内容
点这里 ↓↓↓ 记得 关注✔ 标星⭐ 哦~