swoole hook 不生效会导致 redis 失效,因协程需通过 swoole_hook_flags 显式接管 stream_socket_client 等 i/o 调用,否则退化为同步阻塞或报错;须正确配置钩子、禁用 disable_functions、指定 phpredis 驱动并关闭短名。

Hyperf 启动时报 Class 'Swoole\Http\Server' 找不到,或 Redis 操作卡死、超时、返回空值——大概率不是扩展没装,而是 SWOOLE_HOOK_FLAGS 没设对,或被 PHP 配置拦住了。
为什么 Swoole Hook 不生效会导致 Redis 失效
Swoole 协程要接管所有 I/O(包括 Redis 的 socket 读写),必须通过 SWOOLE_HOOK_FLAGS 显式开启对应子系统的钩子。Redis 客户端底层走的是 stream_socket_client 或 fsockopen,如果没开启 SWOOLE_HOOK_STREAM 或 SWOOLE_HOOK_SOCKET,协程就无法拦截这些调用,结果就是:要么退化成同步阻塞,要么直接抛错(如 Connection refused)。
常见错误现象:
-
Redis::connect()耗时数秒后失败,日志里出现php_network_getaddresses: getaddrinfo failed - 使用
Hyperf\Redis\Redis时,get()返回null但无报错 - 并发请求下 Redis 连接数暴涨,
lsof -p {pid} | grep redis显示大量TCP连接未释放
正确配置 SWOOLE_HOOK_FLAGS 的三种方式
优先级从高到低:代码中设置 > .env > php.ini。推荐统一在 .env 中管理,避免环境差异。
- 在
.env文件中写入:SWOOLE_HOOK_FLAGS=SWOOLE_HOOK_ALL(最稳妥,覆盖全部子系统) - 若需精细控制(如禁用文件钩子防干扰),可显式列出:
SWOOLE_HOOK_FLAGS=SWOOLE_HOOK_STREAM|SWOOLE_HOOK_SOCKET|SWOOLE_HOOK_CURL - 切勿在
config/autoload/coroutine.php中用Runtime::enableCoroutine()二次设置——Hyperf 已在启动时自动调用,重复设置会覆盖.env值 - 确认
php.ini中未禁用关键函数:disable_functions里不能含stream_socket_client、fsockopen、curl_init
验证 Hook 是否真正生效
光看 php --ri swoole 输出不够,得测行为:
- 执行
php bin/hyperf.php server:info,检查输出中hook_flags字段是否为十进制数值(如65535对应SWOOLE_HOOK_ALL),而非字符串"SWOOLE_HOOK_ALL" - 写一个测试接口,用
Swoole\Coroutine\Redis直连 Redis 并执行setex('test', 10, 'ok'),再立刻用Hyperf\Redis\Redis调用get('test')—— 两者必须都能读写成功才算通路完整 - 用
strace -p {worker_pid} -e trace=epoll_wait,connect,sendto,recvfrom观察:正常协程模式下,connect应极少出现,epoll_wait高频轮询,sendto/recvfrom成对出现且延迟低
容易被忽略的兼容性雷区
即使 SWOOLE_HOOK_FLAGS 设对了,以下两点仍可能让 Redis 在协程中“假装工作”:
-
hyperf/redis包默认使用Predis作为驱动,而 Predis 是纯 PHP 实现,不走 Swoole 协程网络栈——必须在config/autoload/redis.php中强制指定'driver' => 'phpredis',并确保phpredis扩展已启用(php -m | grep redis) - Swoole ≥ 5.0 后,默认关闭短名(
swoole.use_shortname = Off),若php.ini里还开着,会导致Swoole\Coroutine\Redis类找不到,报Class not found
Hook 是开关,不是万能胶。它只负责把同步调用转成协程调度,底层驱动、扩展状态、PHP 配置,三者缺一不可。











