根本原因是Worker状态未外移到共享存储层,各机房Worker彼此不可见;必须通过Redis Cluster注册带TTL的节点键,并用SCAN分批拉取活跃节点,配合Kafka中继和Lua原子操作保障跨机房任务一致性。

Workerman 任务进程无法跨机房感知在线 Worker
根本原因不是 Workerman 本身不支持,而是默认的 Worker::getAllWorkerPids() 或 ConnectionInterface 列表只返回本机进程连接,跨机房 Worker 彼此不可见。你不能靠“遍历所有 Worker 进程”来派发任务——那只会漏掉另一个机房的节点。
必须把 Worker 状态外移到共享存储层:每个 Worker 启动时向 Redis Cluster 写入带 TTL 的注册键(如 worker:shanghai:pid1234),并定期续期;任务调度器通过 SCAN 或 KEYS worker:*:(慎用)+ EXISTS 拉取活跃节点列表。注意:redis-cli --cluster 初始化的集群才支持多机房分布,单点 Redis 或主从复制都不行。
- 所有注册键必须带过期时间(建议 30s),避免 Worker 异常退出后残留死键
- 不要用
getallkeys,高并发下会阻塞 Redis;改用SCAN分批拉取 - PHP 的
ext-redis必须 ≥ 5.3.2 才支持 Redis Cluster 自动重定向
任务消息在跨机房间丢失或重复
直接用 $redis->publish('task:channel', $msg) 在双机房场景下必然失败:Redis Pub/Sub 不跨集群,也不持久化。A 机房发的消息,B 机房的订阅者收不到。
正确做法是“本地发布 + 中继兜底”:每个机房部署一个独立的 Redis Pub/Sub 通道(task:shanghai / task:beijing),同时接入 Kafka 或 RabbitMQ 镜像队列作为跨机房中继。Worker 收到本地 channel 消息后,先处理;若失败,则投递到 Kafka 的 task-fallback topic,由另一机房的消费者拉取重试。
立即学习“PHP免费学习笔记(深入)”;
- Kafka 要启用
min.insync.replicas=2和acks=all,确保跨机房写入至少落盘两副本 - 不要让 Worker 直连异地 Kafka broker;每个机房只连本地 broker,靠镜像队列同步
- 任务消息体里必须带
trace_id和retry_count字段,用于幂等和死信判断
任务状态不一致:一个机房看到成功,另一个机房显示失败
问题出在状态存储没做分布式事务或最终一致性保障。比如 Worker 处理完任务后更新 MySQL 状态,但该库只部署在 A 机房,B 机房查不到最新值。
状态必须统一存到跨机房高可用的 Redis Cluster,并用 Lua 脚本保证原子性操作。例如更新任务状态时,不能分两步:SET task:123 status processing + EXPIRE task:123 3600;而要用 EVAL "redis.call('set', KEYS[1], ARGV[1]); redis.call('expire', KEYS[1], ARGV[2])" 1 task:123 processing 3600。
- MySQL 双写风险极高,除非用 MGR 或 TiDB,否则别碰跨机房强一致写入
- 所有状态键名要带机房前缀(如
status:shanghai:task:123),避免 key 冲突 - 定时任务(如超时检查)必须每个机房独立运行,但只处理本机房注册的任务键
心跳与故障剔除不同步导致脑裂
两个机房的 Worker 都认为自己是“唯一存活”,是因为心跳检测没对齐时钟或没共享判定逻辑。NTP 校时偏差 > 500ms 就可能让 A 机房认为 B 的 Worker 已下线,而 B 机房还在续期。
心跳必须走独立服务(如 Consul Health Check 或自建 HTTP 健康端点),且所有机房共用一套判定规则:比如连续 3 次未收到心跳(间隔 10s),且该 Worker 注册时间戳早于当前时间 - 35s,才标记为离线。Redis 中的状态键要同时记录心跳时间戳(HSET worker:shanghai:123 last_heartbeat 1753670187)和版本号,避免并发覆盖。
- 禁止用
time()做超时判断,必须用microtime(true)并校准 NTP - Consul 的健康检查路径要返回 JSON,含
status和timestamp字段,供其他机房拉取比对 - 故障剔除动作(如踢出任务队列)必须通过 Redis 的
PUBLISH触发广播,而不是本地直接删 key
跨机房容灾最易被忽略的不是技术选型,而是“时间漂移”和“状态键命名空间污染”——前者让心跳失效,后者让两个机房互相覆盖对方的状态。这两个点一旦出错,整个任务系统会在无报错的情况下静默降级。



















