概叙
科普文:软件架构设计之【Nginx 实现灰度发布的三种方法总结】-CSDN博客
NGINX简介
NGINX 是一个跨平台的开源 Web 服务器,使用 C 语言开发。据统计,全世界流量最高的前 1000 名网站中,有超过 40% 的网站都在使用 NGINX 处理海量请求。
NGINX 有什么优势,导致它从众多的 Web 服务器中脱颖而出,并一直保持高使用量呢?
核心原因在于,NGINX 天生善于处理高并发,能在高并发请求的同时保持高效的服务。
相比于同时代的其他竞争对手例如 Apache、Tomcat 等,其领先的事件驱动型设计和全异步的网络 I/O 处理机制,以及极致的内存分配管理等众多优秀设计,将服务器硬件资源压缩到了极致。
使得 NGINX 成为高性能 Web 服务器的代名词。
当然,除此之外还有一些其他原因,比如:
- 高度模块化的设计,使得 NGINX 拥有无数个功能丰富的官方模块和第三方拓展模块。
- 最自由的 BSD 许可协议,使得无数开发者愿意为 NGINX 贡献自己的想法。
- 支持热加载,能保证 NGINX 提供 7x24h 不间断的服务。(有待商讨)
关于热加载
前面在总结Nginx的灰度发布中,避免不开“nginx -s reload”这个命令,以及systemctl nginx restart。看一下Nginx reload和restart区别 reload,
如(nginx -s reload)重新加载的意思,reload会重新加载配置文件,nginx服务不会中断,而且reload时会测试conf语法等,如果出错会rollback用上一次正确配置文件保持正常运行。
restart,如(systemctl nginx restart,或nginx -s stop停止,然后nginx命令启动)重启,会重启nginx服务。这个重启会造成服务一瞬间的中断,当然如果配置文件出错会导致服务启动失败,那就是更长时间的服务中断了。
所以在线上部署实施的时候,正常操作都是reload。那么reload是不是Nginx的热加载呢?
热加载功能是什么样的?
首先应该是用户端无感知的,在保证用户请求正常和连接不断的情况下,实现服务端或上游的动态更新。
那什么情况下需要热加载?
在如今云原生时代下,微服务架构盛行,越来越多的应用场景有了更加频繁的服务变更需求。包括反向代理域名上下线、上游地址变更、IP 黑白名单更新等,这些都和热加载息息相关。
显而易见,nginx -s reload命令通过向主进程发送特定的信号来重新加载配置文件,实现了在不中断服务的情况下更新配置,这是一种典型的热加载机制。那么 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 ;下面我们大概了解一下Apache APISIX,后面再单独详细梳理这个技术和应用实战。
NGINX 热加载之云原生网关:Apache APISIX
Apache APISIX 是基于 NGINX + Lua 的技术栈,以 ETCD 作为配置中心实现的云原生、高性能、全动态的微服务 API 网关,提供负载均衡、动态上游、灰度发布、精细化路由、限流限速、服务降级、服务熔断、身份认证、可观测性等数百项功能。同时,它也支持作为 K8s Ingress Controller 来使用。是真正意义上的云原生 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软件架构
APISIX 的架构主要分成两部分:
- 第一部分叫做数据面,它是真正去处理来自客户端请求的一个组件,去处理用户 的真实流量,包括像身份验证、证书卸载、日志分析和可观测性等功能。数据面 本身并不会存储任何数据,所以它是一个无状态结构。
- 第二部分叫做控制面。APISIX 在底层架构上和其它 API 网关的一个很大不同就 在于控制面。APISIX 在控制面上并没有使用传统的类似于像 MySQL 去做配置 存储,而是选择使用 etcd。
APISIX特征
APISIX典型场景的描述
比如增加某个新域名的反向代理,在 APISIX 中只需创建上游,并添加新的路由即可,整个过程中不需要 NGINX 进程有任何重启。
再比如插件系统,APISIX 可以通过 ip-restriction 插件实现 IP 黑白名单功能,这些能力的更新也是动态方式,同样不需要重启服务。借助架构内的 ETCD,配置策略以增量方式实时推送,最终让所有规则实时、动态的生效,为用户带来极致体验。
Apache APISIX是一个动态、实时、高性能的API网关,用于处理API路由、访问控制、流量控制等任务。以下是一些使用Apache APISIX的场景:
-
服务路由与负载均衡:Apache APISIX可以作为服务路由和负载均衡器,将请求转发到不同的上游服务。
-
身份验证和授权:Apache APISIX提供多种身份验证机制,如Key-Auth、JWT认证等,确保API安全性。
-
服务治理:Apache APISIX提供了服务容错、限流限速、负载均衡等服务治理能力。
-
可观察性和分析:Apache APISIX提供了丰富的可观察性和分析工具,如Prometheus、OpenTracing等,帮助运维人员监控系统。
-
网关扩展:Apache APISIX提供了灵活的插件机制,允许开发者根据需要开发自定义插件。
以下是一个简单的示例,展示如何使用Apache APISIX创建一个路由:
curl -i -X PUT http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: your-api-key' \
-d '{
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
####
这个示例创建了一个路由,将对/hello的请求转发到本地的1980端口。
这只是一个基本的示例,Apache APISIX提供了更多复杂和高级的功能,
需要根据具体需求进行配置。
总结
NGINX 的热加载在某些场景下会长时间存在新旧两套进程,导致额外消耗资源,同时频繁热加载也会导致小概率业务丢失。
面对当下云原生和微服务的技术趋势下, 服务变化更加的频繁,控制 API 的策略也发生了变化,导致我们对热加载的需求提出了新需求,NGINX 的热加载已经不能满足实际业务需求。
现在是时候切换到更贴合云原生时代并且更完善的热加载策略、性能表现卓越的 API 网关——Apache APISIX,从而享受动态、统一管理等特性带来的管理效率上的极大提升。