fiberphp / redis
FiberPHP Redis client manager: sync/async drivers, RESP protocol, heartbeat keepalive and auto-reconnect.
dev-master
2026-08-22 08:10 UTC
Requires
- php: >=8.3
- ext-redis: *
- fiberphp/framework: dev-master
Requires (Dev)
- phpunit/phpunit: ^11.0
This package is not auto-updated.
Last update: 2026-08-22 11:34:33 UTC
README
FiberPHP 框架的 Redis 客户端子包。提供同步(Sync)和异步(Async)双驱动客户端,实现 RESP 协议编解码,支持心跳保活、断线自动重连、协程挂起/恢复,通过 config/redis.php 声明式管理多连接。
特性
- 双驱动:Sync 基于
ext-redis同步阻塞;Async 基于 WorkermanAsyncTcpConnection非阻塞 - RESP 协议:自实现 RESP3 编解码器,支持简单字符串、错误、整数、批量字符串、数组
- 心跳保活:Sync 通过
Timer::add定期 ping,防止连接被 Redis 主动断开 - 自动重连:Sync 命令异常时按指数退避重试(最多 3 次);Async 断线后延迟 5s 自动重连
- 协程挂起:Async 在
revolt/event-loop可用时,命令调用自动挂起当前协程,响应到达后恢复,使异步 Redis 调用像同步代码一样书写 - 订阅模式:Async 支持
subscribe/pSubscribe频道和模式订阅 - 连接探活:
RedisProvider启动时 ping 探活,fail_fast=true时探活失败终止 Worker 启动 - 多连接:通过
config/redis.php声明 default 及更多命名连接(cache / queue 等由应用层按需添加)
环境要求
- PHP >= 8.3
ext-redisfiberphp/frameworkdev-master
安装
composer require fiberphp/redis
安装后 PackageInstaller::discover 自动把 config/redis.php 拷贝到应用 config/redis.php(幂等不覆盖),并通过 PackageManifest 注册 RedisProvider。
配置
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' => 2,
'wait_timeout' => 600, // 异步命令等待超时(秒,仅 Async)
'ping' => 55, // 心跳间隔(秒,仅 Sync)
'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 | ✓ | — | 心跳间隔(秒) |
ssl | ✓ | ✓ | 是否启用 TLS |
使用
同步客户端
// 获取 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');
异步客户端(回调模式)
// 回调模式:命令执行后回调通知
$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 | 无(命令队列驱动) |
| 重连 | 指数退避重试 3 次 | 延迟 5s 自动重连 |
| 超时 | connect_timeout | connect_timeout + wait_timeout |
| 订阅 | 不支持 | subscribe / pSubscribe |
| BRPOP | 不支持 | 支持(协程挂起) |
| 适用 | 简单场景 / 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