Hyperf 的 Redis 客户端实现“同步写法、协程执行”,因其底层未调用 PHP redis 扩展,而是基于 Swoole 协程 Hook 劫持 socket 调用,在 read 时自动让出协程、数据就绪后唤醒,全程非阻塞;前提是必须运行于 Swoole/Swow 协程环境(如 php bin/hyperf.php start),且所有 IO 操作须在 handle() 等协程上下文中执行,不可在构造函数或非协程 CLI 脚本中调用。

Hyperf 的 Redis 客户端不是“同步阻塞”,而是“同步写法、协程执行”——只要在协程环境里调用,get、set 这些方法内部走的全是非阻塞 IO,不会卡住整个进程。
为什么 $redis->get() 看起来像同步却不会阻塞?
它底层没调用 PHP 的 redis 扩展,而是走 Swoole 协程 Hook + Hyperf 封装的协程驱动。当遇到网络 read 时,Swoole 自动让出当前协程,调度其他协程运行;数据回来再唤醒。你写的代码仍是 $redis->get('key'),但执行路径全程异步。
- 前提是服务必须运行在 Swoole 或 Swow 协程模式下(
php bin/hyperf.php start默认就是) - 不能在 CLI 同步脚本或非协程上下文中直接调用——比如在
Command类里没启协程就调get,会退化成同步阻塞 - 检查是否真在协程中:在
handle()或__invoke()里调用是安全的;在__construct()中调用则大概率出问题
handle_timeout 不是超时重试,而是强制终止执行
这个配置只在 async-queue 的 handle() 方法里生效,作用是:一旦任务执行超过设定秒数,协程会被强制中断,并把任务移入 failed 队列。
- 它和 Redis 的
read_timeout无关,后者控制单次 socket 读等待时间 - 若你在
handle()里做了耗时 DB 查询或 HTTP 调用,handle_timeout必须大于它们的预期耗时,否则还没查完就被 kill - 默认值是 10 秒,但高并发场景建议设为
30,并配合日志记录实际耗时,再反向调优
别在 Job 构造函数里触发 Redis 调用
Job 对象会被序列化后存进 Redis,构造函数里调 $redis->get() 会导致两个问题:一是序列化失败(Redis 实例不可序列化),二是破坏任务可重试性——构造阶段失败,任务根本进不了队列。
- 所有 IO 操作(
get、set、DB 查询)必须严格放在handle()内 - 构造函数只允许接收简单类型参数:int、string、array(不含资源/对象/闭包)
- 如果确实需要预加载数据,应在
handle()开头用ApplicationContext::getContainer()->get(Redis::class)获取实例再操作
真正容易被忽略的是:协程生命周期和连接池归属必须对齐。比如 async-queue 没显式配 'redis' => ['pool' => 'default'],它就会新建连接池,导致 Redis 连接数翻倍、wait_timeout 失效、甚至静默丢任务——这不是 bug,是配置缺失下的必然行为。


















