Search by

fiberphp / redis

fiberphp

📮 FiberPHP Redis 客户端 —— 同步/异步双驱动、RESP 协议解析、心跳保活、自动重连。

Package info

gitee.com/fiberphp/redis.git

Issues

pkg:composer/fiberphp/redis

Statistics

Installs: 1

Dependents: 6

Suggesters: 2

dev-master 2026-09-09 05:55 UTC

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 基于 Workerman AsyncTcpConnection 非阻塞
  • 协程自适应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.1
  • fiberphp/framework dev-master
  • ext-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,
];
字段SyncAsync说明
hostRedis 主机地址
portRedis 端口
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

SyncAsync
底层ext-redis(同步阻塞)Workerman AsyncTcpConnection(非阻塞)
协程不支持(同步调用)支持 revolt/event-loop 挂起/恢复
心跳Timer::add 定期 pingTimer::add 定期 ping
重连指数退避重试 3 次延迟 5s 自动重连 + 订阅恢复
超时connect_timeoutconnect_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