webman用redis-queue插件实现延迟队列最省事,需运行消费者进程php webman start queue -d;手动zset实现更灵活但需注意时间戳精度、原子操作及进程异常处理。

Webman 用 webman/redis-queue 插件实现延迟队列
直接装插件是最省事的方案,适合 Webman 项目且不需要自研调度逻辑的场景。它把 Redis 的 ZSet 封装成开箱即用的延迟投递接口,底层自动处理时间排序与重试。
常见错误现象:Redis::send($queue, $data, 60) 投递后消息没被消费;max_attempts 设为 3 却重试了 5 次;延迟时间误差超过 10 秒。
- 必须确保已运行消费者进程:
php webman start queue -d,否则延迟消息永远卡在ZSet里不触发 -
retry_seconds是倍增间隔(第 n 次重试等待n * retry_seconds秒),不是固定间隔,别误当成常量重试周期 - 延迟精度依赖消费者轮询频率,默认每 200ms 扫一次
ZSet,若积压严重或 CPU 负载高,实际延迟可能漂移 1–3 秒 - 配置中
host必须写完整 URI 格式,如redis://127.0.0.1:6379,不能只写127.0.0.1,否则连接失败静默忽略
手动用 ZSet 实现延迟队列(绕过插件)
当需要精细控制任务结构、支持多租户隔离、或插件不满足重试策略时,建议手写 ZSet 逻辑。核心就是用时间戳做 score,用序列化数据做 member,再起一个独立 worker 进程持续 ZRANGEBYSCORE 扫描。
使用场景:订单超时关单、会员状态批量刷新、跨服务回调重试。
- 投递时用
$redis->zAdd($key, $execute_at_timestamp, json_encode($job)),注意$execute_at_timestamp是绝对时间戳(秒级),不是相对秒数 - worker 进程里必须用
ZPOPMIN或ZRANGEBYSCORE ... LIMIT 1配合ZREM原子操作,避免同一任务被多个进程重复取走 - 别用
sleep(1)轮询——CPU 空转;改用usleep(200000)(200ms)+ZCARD判断是否有待处理任务,空闲时降频 - worker 进程异常退出会导致正在处理的任务丢失,建议加
pcntl_signal捕获SIGTERM,退出前把当前 job 写回ZSet并设稍后时间
Client::send() 异步投递的适用边界
Client::send() 不是“更高级的投递方式”,而是明确为低可靠性场景设计的内存缓冲层。它和 Redis::send() 是两类用途,混用会出问题。
容易踩的坑:Client::send() 返回后立刻 reload 进程,发现刚投的 10 条消息全丢了;用它投支付结果通知,用户收不到短信。
- 仅适用于日志上报、埋点统计、非关键通知等允许丢失的场景
- 本地内存队列默认大小是 1000 条,超限后新消息直接丢弃,不会阻塞或报错
- 它不校验 Redis 连通性,即使 Redis 宕机,
Client::send()仍返回成功,这点和Redis::send()的强依赖完全不同 - 若业务要求“至少一次”送达,必须用
Redis::send()+ 失败重试 + 幂等 key,而不是换Client::send()
延迟队列里最容易被忽略的时区与精度问题
所有基于时间戳的延迟逻辑,一旦服务器时区不统一、PHP 与 Redis 时间不同步、或用了毫秒级时间戳但 ZSet 只支持秒级 score,就会导致任务提前或永久不触发。
真实案例:上海服务器 PHP 用 time()(秒),但 Redis 客户端被设成 setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP),反序列化时把整数时间戳当对象解析,score 变成 0。
- 统一用
time()获取秒级时间戳,不要用microtime(true)或 Carbon::now()->timestamp,除非你确认ZSetscore 存储精度能匹配 - 检查 Redis 服务器时区:
docker exec -it redis redis-cli time,输出应与date命令一致,否则score和当前时间比对失效 - Worker 进程里每次扫描前先调
$redis->time()拿 Redis 服务端时间,用它做ZRANGEBYSCORE的边界,而非 PHP 的time() - 测试时别只测“60 秒后执行”,一定要测“1 秒后执行”和“当前时间戳+0”,验证边界逻辑是否漏掉刚好到期的任务
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











