php 使用webman框架,redis键过期监听

26 阅读1分钟

1、服务器上redis配置

在 redis.conf 中配置

notify-keyspace-events Ex

或者通过命令行临时设置

redis-cli config set notify-keyspace-events Ex

2、框架中创建监听器

// app/listener/RedisKeyExpiredListener.php

namespace app\listener;

use support\Redis;
use Webman\Event\Event;

class RedisKeyExpiredListener
{
    public function handle($event)
    {
        $key = $event['key'];
        $channel = $event['channel'];
        
        // 这里处理 key 过期的业务逻辑
        echo "Key expired: {$key} at " . date('Y-m-d H:i:s') . "\n";
        
        // 示例:处理订单超时
        if (strpos($key, 'order:') === 0) {
            $this->handleOrderTimeout($key);
        }
        
        // 示例:处理会话过期
        if (strpos($key, 'session:') === 0) {
            $this->handleSessionExpired($key);
        }
    }
    
    private function handleOrderTimeout($key)
    {
        // 提取订单ID
        $orderId = str_replace('order:', '', $key);
        
        // 处理订单超时逻辑
        // 例如:更新订单状态为已取消
        echo "处理订单超时: {$orderId}\n";
        
        // 这里可以调用你的业务逻辑
        // Order::where('id', $orderId)->update(['status' => 'cancelled']);
    }
    
    private function handleSessionExpired($key)
    {
        // 处理会话过期逻辑
        $sessionId = str_replace('session:', '', $key);
        echo "会话过期: {$sessionId}\n";
    }
}

3、redis订阅

// app/service/RedisExpireSubscriber.php

namespace app\service;

use support\Redis;
use Workerman\Worker;
use Webman\Event\Event;

class RedisExpireSubscriber
{
    public function onWorkerStart()
    {
        // 只在其中一个 worker 进程中启动订阅
        if (0 === Worker::$id) {
            $this->subscribeKeyExpire();
        }
    }
    
    private function subscribeKeyExpire()
    {
        // 创建独立的 Redis 连接用于订阅
        $redis = new \Redis();
        $redis->connect('127.0.0.1', 6379);
        
        // 订阅键过期事件
        $redis->psubscribe(['__keyevent@*__:expired'], function ($redis, $pattern, $channel, $key) {
            // 触发事件处理
            Event::emit('redis.key.expired', [
                'key' => $key,
                'channel' => $channel,
                'timestamp' => time()
            ]);
        });
    }
}

4、配置事件监听

// config/event.php

return [
    'redis.key.expired' => [
        [app\listener\RedisKeyExpiredListener::class, 'handle'],
    ],
];

5、注册服务

// config/process.php

return [
    // ... 其他进程配置
    
    'redisExpireSubscriber' => [
        'handler' => app\service\RedisExpireSubscriber::class,
        'count' => 1, // 只需要一个进程来订阅
    ],
];

6、设置带过期时间的key

// 示例:设置订单超时
use support\Redis;

class OrderService
{
    public function createOrder($orderId, $expireSeconds = 1800)
    {
        // 创建订单逻辑...
        
        // 设置 Redis key 并指定过期时间
        Redis::setEx("order:{$orderId}", $expireSeconds, 'pending');
        
        echo "订单 {$orderId} 已创建,将在 {$expireSeconds} 秒后过期\n";
    }
}

7、Example

// app/controller/OrderController.php

namespace app\controller;

use support\Request;
use support\Redis;

class OrderController
{
    public function create(Request $request)
    {
        $orderId = uniqid('order_');
        $data = $request->post();
        
        // 保存订单到数据库
        // Order::create([...]);
        
        // 设置 Redis 过期监听(30分钟过期)
        Redis::setEx("order:{$orderId}", 1800, json_encode([
            'order_id' => $orderId,
            'status' => 'pending',
            'created_at' => time()
        ]));
        
        return json([
            'code' => 0,
            'msg' => '订单创建成功',
            'data' => ['order_id' => $orderId]
        ]);
    }
}