Workerman 中必须使用异步 Redis 客户端(如 amphp/redis),因 phpredis 阻塞会卡住事件循环;需在 onWorkerStart 初始化连接池,每次使用前 ping 检测并重建失效连接,用 pipeline 提升批量性能,错误须显式处理。

Workerman 里不能直接用 phpredis 的阻塞连接
Workerman 是纯异步事件驱动模型,phpredis 默认的连接方式是同步阻塞的,一调用 get() 或 set() 就卡住整个进程,根本没法配合 Worker::onMessage 或定时器做并发处理。
常见错误现象:Redis::get(): read error on connection 或请求明显变慢、超时、CPU 占满但无响应——本质是 Redis I/O 挡住了 event loop。
- 必须改用非阻塞客户端,比如
redis-async(基于amphp/redis)或co/redis(协程版,需 Swoole) - Workerman 原生不支持协程,所以
co/redis不能直接用;得选真正基于ReactPHP或Amp的异步 Redis 客户端 - 如果硬要用
phpredis,只能通过子进程(Process)隔离 Redis 调用,但开销大、状态难共享、调试麻烦
推荐方案:用 amphp/redis + Workerman 的 onWorkerStart 启动连接池
amphp/redis 是目前最稳的纯异步 Redis 客户端,和 Workerman 兼容性好,不需要协程运行时,靠回调+Promise 驱动。
使用场景:需要在 Worker::onMessage 中高频读写 Redis(如会话校验、频控计数、消息队列中转)。
- 别在每次
onMessage里 new 一个RedisClient—— 连接开销大,还可能触发连接数上限 - 应在
Worker::onWorkerStart中初始化连接池,复用Amp\Redis\RedisClient实例(它本身线程安全,可被多个回调共用) - 注意 PHP 版本兼容性:
amphp/redis ^2.0要求 PHP >= 8.0;若用 PHP 7.4,得锁死^1.5并搭配amphp/amp:^2 - 连接失败不会抛异常,而是返回 rejected Promise,务必用
Promise\rethrow或catch处理,否则静默失败
示例片段:
use Amp\Redis\RedisClient;
use Amp\Promise;
// 在 onWorkerStart 中
$redis = RedisClient::connect('tcp://127.0.0.1:6379');
// 在 onMessage 中调用
$redis->get('user:1001')->onResolve(function ($error, $value) {
if ($error) {
// 记 log,别忽略
error_log('Redis get failed: ' . $error->getMessage());
return;
}
echo "Got: $value";
});
连接断开后不会自动重连,得自己兜底
amphp/redis 的 client 实例是“一连接一对象”,底层 socket 断开后,该实例就不可用了,后续所有操作都会 reject,且不会自动重建连接。
容易踩的坑:本地测试一切正常,上生产后 Redis 重启或网络抖动,服务就持续报错,直到 Worker 进程重启。
- 不能依赖 client 实例长期存活,要在每次使用前检查是否可用(比如发个
PING) - 更稳妥的做法是封装一层
getRedisClient()工厂函数,在内部缓存 client,但加 try/catch + ping 判断,失败则重建 - 别把 client 存进全局变量或静态属性再跨 Worker 共享——Workerman 的每个 Worker 是独立进程,内存不互通
- 如果用了连接池(如
amphp/pool),要注意 pool 的最大连接数和空闲回收策略,避免 Redis 端连接数打满
批量操作要用 pipeline,但别误用 multi/exec
异步客户端的 pipeline 是真并行发送、顺序收包,能显著降低 RTT;而 multi/exec 在 amphp/redis 中仍是串行命令流,且不支持 WATCH,实际意义不大。
性能影响明显:10 次单 key get 并行 pipeline 比 10 次单独调用快 3–5 倍;但 multi/exec 只是语法糖,反而多一次 round-trip。
- 用
$client->pipeline()->get('a')->get('b')->exec(),返回的是 Promise,不是 Array - pipeline 不保证原子性,只是减少网络开销;需要事务语义,得靠 Lua 脚本(
eval)实现 - 别在 pipeline 里混用写命令和读命令——虽然语法允许,但结果顺序难预测,debug 成本高










