优雅关闭与排空
AISIX 分阶段关闭,使负载均衡器能够在不中断网关已接受工作的情况下撤出实例。网关先变为未就绪,在排空期间继续处理请求和长连接会话,然后关闭监听器,并完成所有快照缓存写入。此流程同样适用于 AISIX OSS,以及连接到 AISIX Enterprise 或 AISIX Cloud 的网关。
请同时设置网关排空窗口和部署平台的终止期限。排空窗口给流量层留出停止路由的时间;平台期限则让网关完成请求、流式传输、Realtime 会话、空闲连接和快照持久化。
了解关闭流程
收到 SIGTERM 或 SIGINT 时,网关会:
- 立即让
/readyz返回503 Service Unavailable,使下一次健康检查撤出该实例。/livez继续返回200:进程仍然健康,排空期间不得重启。 - 继续接受新连接,至少持续
shutdown.min_drain_secs,默认为 30 秒。 - 在 HTTP/1.1 响应中添加
Connection: close,并向 HTTP/2 客户端发送GOAWAY帧,使客户端停止复用连接而不中断进行中的工作。WebSocket 升级(例如 Realtime 握手)仍会正常完成。 - 最小窗口结束后,网关自身不设期限地等待进行中请求数降为零,然后停止接受新连接。流式响应在结束前一直计为进行中;打开的 Realtime WebSocket 会话(
/v1/realtime)从握手开始到关闭并记录用量期间也一直计为进行中。 - 等待已经接受的连接完成。通过其中一条连接到达的请求会正常处理,并返回
Connection: close;在其中一条连接上打开的 Realtime 会话会让网关保持运行,直到该会话关闭。空闲的连接池连接会被关闭。尚未发送请求的连接会一直保留,直到其发送请求、downstream.idle_timeout_secs到期,或客户端关闭连接。 - 对使用 etcd 或连接控制面且保留快照缓 存的网关,最多再等待 5 秒,使仍在进行的缓存写入落盘,然后退出。
外部负载均衡器的健康检查应指向 /readyz,而不是仅建立 TCP 连接。TCP 检查无法观察就绪状态,只能看到监听器关闭,而排空窗口正是为了延迟这一事件。完整的存活与就绪约定参见健康检查。
配置排空窗口
请把 min_drain_secs 设置为大于 /readyz 状态变化后负载均衡器停止路由所需的时间。对于 Kubernetes,该时间为 periodSeconds x failureThreshold;对于外部负载均衡器,请使用检查间隔乘以重试次数。
shutdown:
min_drain_secs: 30
该窗口是最小值,而不是关闭期限。窗口结束后,网关仍会等待进行 中请求数降为零。设置为 0 会取消最小窗口,仅适用于没有任何依赖健康检查的系统向该实例路由流量的情况。
设置平台期限
网关自身对进行中工作不设等待期限,整个流程的实际上限由部署平台决定:Kubernetes 中是 terminationGracePeriodSeconds,systemd 下是 TimeoutStopSec。
该期限应大于 shutdown.min_drain_secs、你希望保留的最长请求、流式传输或 Realtime 会话时长,以及启用持久化时的 5 秒快照缓存排空这三者之和。请额外留出运维余量,并加上任何 preStop 钩子的持续时间,因为该钩子会消耗同一个 Kubernetes 终止预算。
平台宽限期结束时仍打开的 Realtime 会话会随进程一起关闭,与流式响应相同。
考虑空闲连接与 TLS 连接
在默认的 downstream.idle_timeout_secs: 0 下,一条已打开却从不发送请求的连接会让正在排空的网关一直保持运行,直到客户端关闭连接或平台终止进程。负载均衡器预先建立的连接就是一个例子。
设置 downstream.idle_timeout_secs 可以为这段等待设定上限。在 TLS 监听器上,该超时在 TLS 握手完成后生效;始终未完成握手的连接不受它约束。
完成快照缓存写入
快照缓存预留时间是额外时间,因为它只在进行中请求结束后才开始计算。5 秒上限是固定的。超时后,网关会记录警告并退出。原子文件替换可以避免产生不完整的快照,但下一个进程可能恢复较旧的快照,或没有可恢复的快照。
在日志中观察排空
使用排空日志字段区分尚未完成的请求与仍保持打开或继续到达的连接:
| 消息 | 字段 | 写出时机 |
|---|---|---|
draining — /readyz now reports 503, still accepting new connections | min_drain_secs、in_flight、open_connections | 收到信号时写出一次。 |
still draining in-flight requests | in_flight、open_connections | 排空期间定期写出。 |
request arrived while draining | method、path,以及请求自身的 request_id、peer 和 downstream_request_id | 收到信号后,每个到达网关的请求写出一条。 |
accepted a new downstream connection while draining | peer、open_connections | 收到信号后,每个新接受的连接写出一条。 |
in_flight 统计活跃请求,包括流式 响应和打开的 Realtime 会话。open_connections 还包括空闲的连接池连接和健康探针连接。如果 in_flight 持续大于零,AISIX 仍在完成已接受的工作;如果 open_connections 增长,说明其他组件仍在向排空中的实例建立连接。
排查滚动更新期间的失败请求时,请通过 peer 匹配 request arrived while draining 与 accepted a new downstream connection while draining。存在匹配的接受记录,表示连接在排空开始后到达;如果不存在,则客户端复用了较早的连接池连接。健康检查可能产生接受记录,但不会产生请求到达记录。
peer 和 downstream_request_id 提供访问日志与请求关联中介绍的到达上下文。将 peer 与前置代理的连接记录匹配,将 downstream_request_id 与其请求记录匹配。