不能。Workerman仅是高性能PHP网络通信框架,负责低延迟消息收发,不提供地理围栏、权重排序、并发锁、状态一致性等抢单匹配能力,需后端服务+Redis/数据库+地理索引协同实现。

Workerman 能不能直接做抢单匹配核心?
不能。Workerman 是高性能 PHP 网络通信框架,本质是 I/O 多路复用的长连接服务器,它不提供业务逻辑层面的“订单-司机”实时匹配能力——比如地理围栏计算、权重排序、并发锁防重复抢、状态一致性校验等。它只负责把 driver_id 和 order_id 的消息快速、低延迟地推过去、收回来。真正匹配得靠后端服务(PHP 或其他语言)+ 数据库/Redis + 地理索引(如 Redis GEO 或 PostGIS)协同完成。
司机上线和订单发布时,怎么用 Workerman 做实时通道?
关键不是“推送订单”,而是让司机端 WebSocket 连接能被唯一标识,并支持按标签广播。常见错误是直接用 $connection->send() 全局推,结果所有司机都收到同一单——这根本不是抢单,是广播通知。
实操建议:
- 司机登录成功后,在 Workerman 的
onConnect阶段,用$_SESSION或更稳妥的Redis::hSet('driver:online', $driver_id, $connection->id)记录连接映射 - 订单发布时,后端匹配服务算出一批候选司机(例如 5km 内活跃司机),再通过
Worker::getConnectionById($conn_id)->send(...)逐个推送,而不是用foreach ($worker->connections as $conn) - 司机端收到订单后,必须带
timestamp和version字段,防止旧消息覆盖新状态;服务端收到抢答请求前,先查 Redis 中该订单的order:123:status是否仍为pending
为什么抢到后订单状态经常不一致?
典型现象:司机 A 和 B 同时收到订单 123,A 先点“抢”,但 B 的请求因网络快反而先落库,A 成功返回却实际没抢到。这不是 Workerman 的问题,而是缺少分布式锁和原子状态变更。
必须在业务层加控制:
- 用
Redis::set('order:123:lock', $driver_id, ['nx', 'ex' => 5])尝试占锁,失败立即返回“已被抢” - 抢成功后,用 Lua 脚本一次性执行:检查订单状态是否为
pending、写入司机 ID、更新状态为accepted,三步不可拆分 - Workerman 连接里不做数据库写操作,所有状态变更走独立 API 接口,避免连接阻塞导致消息积压
司机端 WebSocket 心跳和掉线重连怎么不丢单?
司机骑车中网络抖动频繁,Workerman 默认 60 秒无心跳就断连,但订单推送可能正在途中。单纯延长 heartbeat_idle_time 不解决问题,会堆积无效连接。
更可靠的做法:
- 客户端每 25 秒发一次
ping包,服务端收到后更新Redis::expire('driver:1001:heartbeat', 45) - 订单推送前,先查
Redis::exists('driver:1001:heartbeat'),不存在则跳过该司机 - 司机重连后,主动发
{ "type": "sync_status", "last_seq": 123 },服务端从 Redis 的order:recent列表中拉取未读订单补推(需记录每个司机的已读seq)

















