fiberphp / redis
📮 FiberPHP Redis 客户端 —— 同步/异步双驱动、RESP 协议解析、心跳保活、自动重连。
Requires
- php: >=8.3
- fiberphp/config: dev-master
- fiberphp/container: dev-master
- fiberphp/contract: dev-master
- fiberphp/discovery: dev-master
- psr/log: ^3.0
- revolt/event-loop: ^1.0
- workerman/workerman: ^5.1
Requires (Dev)
- phpunit/phpunit: ^11.0
Suggests
- ext-redis: Sync 客户端与启动探活所需(协程内 Async 客户端无需扩展)
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-09 05:55:23 UTC
README
FiberPHP 框架的 Redis 客户端子包。提供同步(Sync)和异步(Async)双驱动客户端,实现 RESP 协议编解码,支持心跳保活、断线自动重连、协程挂起/恢复、连接池,通过
config/redis.php 声明式管理多连接。
特性
- 双驱动:Sync 基于
ext-redis同步阻塞;Async 基于 WorkermanAsyncTcpConnection非阻塞 - 协程自适应:
auto()根据 Fiber 上下文自动选择 Sync(协程外)或 Async(协程内) - RESP 协议:自实现 RESP2 编解码器,支持简单字符串、错误、整数、批量字符串、数组
- 心跳保活:Sync 和 Async 均通过
Timer::add定期 ping,防止连接被 Redis 主动断开 - 自动重连:Sync 命令异常时按指数退避重试(最多 3 次);Async 断线后延迟 5s 自动重连,重连后自动恢复 SUBSCRIBE 订阅
- 协程挂起:Async 在
revolt/event-loop可用时,命令调用自动挂起当前协程,响应到达后恢复,使异步 Redis 调用像同步代码一样书写 - 连接池:Sync 支持
syncPool()连接池,借还复用,Fiber 内池满直接抛异常不阻塞 EventLoop - 订阅模式:Async 支持
subscribe/pSubscribe频道和模式订阅,断线重连后自动恢复 - 队列保护:Async 命令队列加上限(
max_queue_size,默认 10000),防止 Redis 慢时 OOM - 连接探活:
RedisProvider启动时 ping 探活,fail_fast=true时探活失败终止 Worker 启动 - 多连接:通过
config/redis.php声明 default 及更多命名连接(cache / queue 等由应用层按需添加)
环境要求
- PHP >= 8.3
workerman/workerman^5.1fiberphp/frameworkdev-masterext-redis(可选,仅 Sync 客户端与启动探活需要;协程内 Async 客户端无需扩展)
说明:
ext-redis为软依赖。协程上下文推荐使用auto()/async()(纯 PHP 实现,无需扩展); 串行上下文(CLI/启动期)使用sync()时若未安装扩展会抛出带引导信息的RedisException。
安装
composer require fiberphp/redis
安装后通过 PackageManifest 自动注册 RedisProvider;包内置 config/redis.php 默认配置由 config 包自动合并,装包即用。如需调整,在应用 config/redis.php 放置同名键即可递归覆盖默认值。
配置
config/redis.php:
return [
// 默认连接
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', 6379),
'auth' => env('REDIS_AUTH'),
'db' => 0,
'connect_timeout' => 3, // 连接超时(秒)
'wait_timeout' => 600, // 异步命令等待超时(秒,仅 Async)
'ping' => 55, // 心跳间隔(秒,Sync 和 Async 均适用)
'max_queue_size' => 10000, // 异步命令队列上限(仅 Async,超限抛 RedisException)
'ssl' => false,
],
// 按需声明更多连接(cache / queue 等业务连接由应用层定义,建议分 db 隔离)
// 'cache' => ['host' => env('REDIS_HOST', '127.0.0.1'), 'db' => 1, ...],
// 探活失败是否终止启动(Redis 是基础设施,默认终止)
'fail_fast' => true,
];
| 字段 | Sync | Async | 说明 |
|---|---|---|---|
host | ✓ | ✓ | Redis 主机地址 |
port | ✓ | ✓ | Redis 端口 |
auth | ✓ | ✓ | 认证密码(无密码留空) |
db | ✓ | ✓ | 默认数据库编号 |
connect_timeout | ✓ | ✓ | 连接超时(秒) |
wait_timeout | — | ✓ | 异步命令等待超时(秒) |
ping | ✓ | ✓ | 心跳间隔(秒) |
max_queue_size | — | ✓ | 异步命令队列上限(超限抛异常) |
ssl | ✓ | ✓ | 是否启用 TLS |
使用
协程自适应(推荐)
// auto() 根据 Fiber 上下文自动选择:
// - 协程内 → Async(非阻塞,不卡 EventLoop)
// - 协程外 → Sync(同步阻塞)
$redis = redis()->auto();
$redis->set('foo', 'bar');
$value = $redis->get('foo');
同步客户端
// 获取 RedisManager → 创建 sync 客户端
$redis = redis()->sync(); // default 连接
$cache = redis()->sync('cache'); // cache 连接(需在 config/redis.php 声明)
// 直接调用 Redis 命令(通过 __call 代理到 ext-redis)
$redis->set('foo', 'bar');
$value = $redis->get('foo');
$redis->del('key1', 'key2');
// 闭包形式(等价)
$value = redis()->sync()->get('foo');
连接池(Sync)
// 创建连接池(默认 max_open=50, max_idle=20)
$pool = redis()->syncPool();
// 借取 → 使用 → 归还
$redis = $pool->borrow();
$redis->set('foo', 'bar');
$pool->return($redis);
// 闭包形式(自动归还)
$pool->borrow(fn($redis) => $redis->get('foo'));
异步客户端(回调模式)
// 回调模式:命令执行后回调通知
$redis = redis()->async();
$redis->get('foo', function ($result, $client) {
echo $result; // 'bar'
});
$redis->set('counter', 1, function ($result) {
// $result === true
});
异步客户端(协程模式)
协程模式下(revolt/event-loop 已加载),不传回调时命令自动挂起当前协程,响应到达后恢复,书写方式与同步一致:
$redis = redis()->async();
// 挂起 → 等待响应 → 恢复,返回结果
$value = $redis->get('foo');
// 链式调用
$count = $redis->incr('counter');
$redis->set("user:{$count}", 'data');
订阅模式
$redis = redis()->async();
// 频道订阅
$redis->subscribe(['news', 'alerts'], function ($channel, $message, $client) {
echo "[{$channel}] {$message}\n";
});
// 模式订阅(通配符)
$redis->pSubscribe(['user:*'], function ($pattern, $channel, $message, $client) {
echo "[{$channel}] {$message}\n";
});
断线重连后订阅自动恢复,无需手动重新订阅。
BRPOP 阻塞弹出
// 协程模式:挂起直到有消息或超时
$result = redis()->async()->brPop(['queue:email'], 0); // 0 = 无限阻塞
if ($result) {
[$key, $value] = $result;
// 处理消息...
}
SCAN 增量扫描
// 不阻塞 Redis,适合大库扫描
$keys = redis()->sync()->scanKeys('prefix:*', 100); // 每次提示 100 条
foreach ($keys as $key) {
// ...
}
获取原生 Redis 实例
$native = redis()->sync()->getRedis();
$native->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP);
$native->setOption(\Redis::OPT_PREFIX, 'app:');
Sync vs Async
| 项 | Sync | Async |
|---|---|---|
| 底层 | ext-redis(同步阻塞) | Workerman AsyncTcpConnection(非阻塞) |
| 协程 | 不支持(同步调用) | 支持 revolt/event-loop 挂起/恢复 |
| 心跳 | Timer::add 定期 ping | Timer::add 定期 ping |
| 重连 | 指数退避重试 3 次 | 延迟 5s 自动重连 + 订阅恢复 |
| 超时 | connect_timeout | connect_timeout + wait_timeout |
| 队列 | — | max_queue_size 上限保护 |
| 订阅 | 不支持 | subscribe / pSubscribe |
| BRPOP | 不支持 | 支持(协程挂起) |
| 连接池 | syncPool() | 单连接多路复用 |
| 适用 | 简单场景 / CLI 脚本 | Worker 内 / 高并发 / 订阅 |
启动探活
RedisProvider 在 Worker 启动时对 default 连接做 ping 探活:
fail_fast = true(默认):探活失败终止 Worker 启动(Redis 是基础设施)fail_fast = false:探活失败仅记录日志,运行时首次命令才会抛异常
超时保护由 BootGuard 通过 pcntl_alarm 统一兜底。
自定义实现
通过 setSyncClass / setAsyncClass 替换实现类(如追踪包装类):
$manager = redis();
$manager->setSyncClass(MyTracingSync::class);
$manager->setAsyncClass(MyTracingAsync::class);
替换类需分别继承 Sync / Async。
License
MIT