Kubernetes通过Job和Deployment分别管理一次性定时任务与长期轮询型延迟任务消费者。Job适用于精确触发场景,配置OnFailure重启策略和TTL自动清理;Deployment用于常驻消费者,需配置健康探针、资源限制及反亲和性;配合Redis ZSet实现延迟队列,须统一时间源并监控积压与失败兜底。

Kubernetes 本身不直接执行 PHP 延迟任务,但它能可靠地调度、运行和保障延迟任务消费者进程的生命周期。关键在于:把“延迟逻辑”(如 Redis ZSet 轮询)从 PHP 应用里剥离出来,封装成独立、可伸缩、带健康检查的 Job 或 Deployment,再由 K8s 统一管理。
明确延迟任务类型,选对控制器
PHP 延迟任务在 K8s 中通常分两类,调度方式完全不同:
一次性定时任务(如订单30分钟后自动取消)
→ 用Job:任务执行完即退出,适合精确触发、不可重复的场景。
→ 配置spec.template.spec.restartPolicy: OnFailure,失败自动重试;加ttlSecondsAfterFinished: 3600自动清理完成态 Job。长期轮询型消费者(如每秒拉取 Redis 延迟队列)
→ 用Deployment:保持常驻进程,支持滚动更新、扩缩容、自动恢复。
→ 必须配livenessProbe和readinessProbe,指向/health或本地状态端口,避免卡死却不被发现。
示例:一个轮询 Redis 的消费者 Pod,若连续 3 次 probe 失败(如 30 秒内没响应),K8s 会自动重启容器,而不是让它挂着不动。
用 Deployment 管理常驻消费者进程
这是最常用、最稳的方式,适用于 ThinkPHP、Laravel 或自研队列系统对接 Redis ZSet 的场景。
立即学习“PHP免费学习笔记(深入)”;
- 容器镜像里只放 CLI 消费脚本(如
php artisan queue:work --queue=default_delay --daemon),不走 FPM。 - 设置资源限制防抢占:
resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" - 加亲和性,让消费者尽量远离高负载 Web 节点:
affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: topologyKey: topology.kubernetes.io/zone labelSelector: matchLabels: app: php-web
让 Job 精准触发延迟动作(配合 CronJob)
如果业务允许“近似延迟”(误差几秒可接受),可用 CronJob + 时间判断组合实现:
- CronJob 每 10 秒跑一次
php /app/bin/check-delayed-tasks.php - 脚本内查 Redis ZSet 中
score <= time()的任务并执行(注意加分布式锁防重复) - 这种方式运维简单、无常驻进程压力,适合低频或非核心延迟任务。
⚠️ 注意:不要用 CronJob 直接
sleep(300)等待——K8s 会认为 Pod 不响应而 kill 掉。
关键配套:可观测与防堆积
延迟任务一旦卡住,用户感知就是“该发的通知没发”“该关的单没关”,必须主动监控:
-
Redis 层:用
zcard queues:default_delay看积压量,zrangebyscore ... LIMIT 0 1查最早到期时间,差值过大说明消费者滞后。 -
Pod 层:通过 Prometheus 抓取消费者日志中的
processed=127或fetched=0字段,Grafana 做「1 分钟内处理数」趋势图。 -
失败兜底:所有延迟任务写入时带上
retry_count和created_at,消费者失败后重新入队(score += 60),超 3 次进死信队列(如queues:default_delay_failed),人工介入。
不复杂但容易忽略的是:延迟任务的“时间源”必须统一。K8s 各节点时钟可能漂移,务必在集群中启用 ntpd 或 chrony 同步,并让所有 PHP 容器挂载宿主机时间(hostPath: /etc/localtime),否则 time() 返回不准,ZSet score 就乱套。



















