优雅关闭
OxPHP 会处理 SIGTERM 和 SIGINT,以便进程退出前先让正在处理的请求完成。这对于零停机部署以及容器编排中的滚动更新至关重要。
信号处理
OxPHP 响应两种关闭信号:
| 信号 | 来源 | 行为 |
|---|---|---|
SIGTERM |
容器编排器、docker stop、kill |
发起优雅关闭 |
SIGINT |
终端 Ctrl+C | 发起优雅关闭 |
两种信号都会触发相同的关闭流程。只需要第一个信号即可,服务器会立即开始排空。
关闭流程
收到关闭信号后,OxPHP 会遵循以下流程:
- 停止接受新连接 —— 服务器停止在主端口上接受新的 TCP 连接。PHP 工作进程继续运行,以处理正在进行中的请求。
- 收尾活跃连接 —— HTTP/2 客户端会收到一个
GOAWAY帧,空闲的 HTTP/1.1 keep-alive 连接会被关闭,这样客户端就会转移到健康的实例,而不是把新请求多路复用到即将关闭的实例上。已打开的流会被迅速且干净地结束 —— 任何已经开始刷新分块输出的响应都算在内,既包括有限长度的下载,也包括 SSE —— 参见 Server-Sent Events。 - 排空正在处理的请求 —— 普通的活跃请求不受打扰,可以带着完整响应正常结束。服务器每 100ms 检查一次是否完成。内部的健康/指标服务器在整个排空期间保持可用,因此就绪探针能够继续工作。
- 强制执行排空截止时间 —— 超过
DRAIN_TIMEOUT_SECONDS后仍在运行的请求会被取消(它们的register_shutdown_function()回调仍会执行),并被给予约 2 秒来收尾,之后服务器继续推进。 - 刷新插件 —— 排空窗口期间缓冲的访问日志条目和 APM span 会被刷新。
- 关闭异步池 —— 后台异步任务池被停止。
- 中止内部服务器 —— 排空完成后,健康/指标服务器被停止。
- 退出 —— 进程以状态码 0 退出。
PHP 工作进程会在第 1 步显式停止 —— 停止主服务器的过程中会调用执行器的 shutdown(),它会通知工作线程在完成任何正在进行的请求后退出。
配置
| 变量 | 默认值 | 说明 |
|---|---|---|
DRAIN_TIMEOUT_SECONDS |
25 |
正在处理的请求被取消前可用于完成的最大秒数;截止时间之后,进程会在约 2 秒内退出。默认值为截止后的收尾和遥测刷新预留了余量,使其能落在 Kubernetes 默认的 30 秒终止宽限期之内 |
将 DRAIN_TIMEOUT_SECONDS 设置为能容纳你预期最慢的请求:
- API 服务器,响应较快:
10–15秒 - 应用程序,涉及文件上传或长查询:
30–60秒 - 工作进程模式,涉及后台处理:与你预期最长的操作相匹配
Kubernetes
在 Kubernetes 中,滚动更新期间的关闭流程如下:
- Kubernetes 向 pod 发送
SIGTERM。 - pod 从 Service 端点列表中被移除。
- OxPHP 在
DRAIN_TIMEOUT_SECONDS之内排空正在处理的连接,然后取消掉落队者并在约 2 秒后退出。 - 如果 pod 在
terminationGracePeriodSeconds之后仍在运行,Kubernetes 会发送SIGKILL。
将 terminationGracePeriodSeconds 设置为高于 DRAIN_TIMEOUT_SECONDS + 2,这样排空 —— 包括截止后的收尾和遥测刷新 —— 就能在强制终止前完成:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
terminationGracePeriodSeconds: 45
containers:
- name: oxphp
image: ghcr.io/oxphp/oxphp:0.10.0
env:
- name: DRAIN_TIMEOUT_SECONDS
value: "30"预停止钩子
如果你的服务从传播端点变更较慢的外部负载均衡器接收流量,可以添加一个预停止钩子来延迟关闭流程:
lifecycle:
preStop:
exec:
command: ["sleep", "5"]这样负载均衡器就有时间在 OxPHP 停止接受连接之前,把该 pod 从其目标列表中移除。
Docker
运行 docker stop 时,Docker 会发送 SIGTERM。Docker 默认的停止超时为 10 秒,超过后 Docker 会发送 SIGKILL。
为了给 OxPHP 足够的时间排空,请增大停止超时:
docker stop --time 45 my-oxphp-container或者在你的 Compose 文件中设置:
services:
oxphp:
image: ghcr.io/oxphp/oxphp:0.10.0
stop_grace_period: 45s
environment:
DRAIN_TIMEOUT_SECONDS: "30"日志消息
在优雅关闭期间,OxPHP 会输出结构化日志消息,你可以对其进行监控:
排空成功:
{"level":"INFO","message":"Received shutdown signal, draining connections"}
{"level":"INFO","message":"Draining in-flight connections","active_connections":3}
{"level":"INFO","message":"All connections drained"}
{"level":"INFO","message":"Server stopped"}达到排空截止时间:
{"level":"INFO","message":"Received shutdown signal, draining connections"}
{"level":"WARN","message":"Drain timeout reached, cancelling in-flight requests","remaining_connections":1}
{"level":"INFO","message":"All connections drained"}
{"level":"INFO","message":"Server stopped"}如果你经常看到 “Drain timeout reached” 警告,请增大 DRAIN_TIMEOUT_SECONDS,或使用 oxphp_request_duration_us 直方图排查长时间运行的请求。