Laravel 7.x 使用 keyspace notification 监听不到 Key 过期事件
场景使用:假设有一个订单 30 分钟以后未付款 自动关闭该订单。或者会员到期自动提醒续费等等。
这个在 Laravel 中其实有更好的选择方式 队列,使用延时队列
ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(30));
但是我还想到一种方案就是使用 Redis
的键空间通知(keyspace notification)。意思就是当 Redis 的 key 删除是,回主动通知发送消息给我们,我们只需要监听订阅对应的事件即可。
接下来我还原事情经过。说一下我遇到的问题,以及最后是如何解决的。
首先 Redis 的 keyspace notification
默认是不开启的。我们需要主动开启,开启方式如下 修改 redis.conf 配置文件 找到 notify-keyspace-events ""
默认是空字符串,表示未开启.
notify-keyspace-events "Ex"
然后重启 Redis
或者直接 redis-cli 下输入如下命令进行修改
redis-cli config set notify-keyspace-events Ex
具体 Ex
代表啥意思 参考如下表格:
字符 |
发送的通知 |
---|---|
K |
键空间通知,所有通知以 keyspace@<db> 为前缀 |
E |
键事件通知,所有通知以 keyevent@<db> 为前缀 |
g |
DEL 、 EXPIRE 、 RENAME 等类型无关的通用命令的通知 |
$ |
字符串命令的通知 |
l |
列表命令的通知 |
s |
集合命令的通知 |
h |
哈希命令的通知 |
z |
有序集合命令的通知 |
x |
过期事件:每当有过期键被删除时发送 |
e |
驱逐(evict)事件:每当有键因为 maxmemory 政策而被删除时发送 |
A |
参数 g$lshzxe 的别名 |
好了,言归正传,接下来在 Laravel 中,我是新建一个 Command 命令
php artisan make:command OrderExpire
内容如下:
<?php
namespace AppConsoleCommands;
use IlluminateConsoleCommand;
use IlluminateSupportFacadesRedis;
class OrderExpire extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'order:expire';
/**
* The console command description.
*
* @var string
*/
protected $description = '处理订单失效';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$redis = Redis::connection('publisher');
Redis::subscribe(['__keyevent@0__:expired'], function ($message, $channel) {
// 处理订单失效 逻辑
echo '订单已失效';
});
}
}
在 config
下 database.php
中 redis 配置里添加 如下内容:
'publisher' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'read_timeout' => 0,
'persistent' => true,
'read_write_timeout' => 0,
],
在 路由文件下 编写 Redis
键 到期命令,设置 5 秒失效
use IlluminateSupportFacadesRedis;
Route::get('/', function () {
Redis::setex('order_2000123421',5,'2000123421');
});
接下来运行行项目
我们首先在 Laravel 项目中运行 控制台命令
php artisan order:expire
接下来在 red-cli 中也监听过期命令
redis-cli
127.0.0.1:6379> psubscribe __keyevent@0__:expired
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyevent@0__:expired"
3) (integer) 1
1) "pmessage"
2) "__keyevent@0__:expired"
3) "__keyevent@0__:expired"
访问路由文件,设置 Redis Key,但是你会发现实际上我们编写的控制台命令,Redis 5 秒过后并不会触发任何事件。而 redis-cli 5秒以后会监听到。
下面是 redis-cli 的结果:
下图是 Laravel 项目的结果,过一定事件还会出现连接错误:
这个问题纠结了挺久。最后解决方案如下:
解决方案
不使用 Laravel 自带的 Redis
门面,改为原生 Redis
。修改 handle 方法
$redis = new Redis();
$redis->connect('redis', '6379');
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
$pattern = '__keyevent@0__:expired';
$redis->psubscribe([$pattern], function ($message, $channel)
{
echo '订单已失效';
});
接下来再次重启命令,访问路由就可以看到自己想要的内容了
Why
I don't know。。。
我猜是 Laravel
的门面 IlluminateSupportFacadesRedis
,这个订阅可能需要配合 发布一起使用。但是我不清楚这个事件订阅如何起作用,总不能直接发布 Redis::publish('test-channel', json_encode(['foo' => 'bar']));
吧?希望遇到的童鞋能和我交流一下。
- OC学习2——C语言特性之函数
- OC学习1——基本数据类型
- QQ定位女友是否回家系列二之定位系统的打造
- htcap:一款实用的递归型Web漏洞扫描工具
- 使用Go和Let's Encrypt证书部署HTTPS
- md5算法
- 《JavaScript高级程序设计》学习笔记(3)——变量、作用域和内存问题
- King Phisher:一款专业的钓鱼活动工具包
- 是不是Bash编程老司机,看完这10条细节就知道了
- 以针对Yahoo! 的安全测试为例讲解如何高效的进行子域名收集与筛选
- 线程池
- hbase 部署
- Hadoop源码系列(一)FairScheduler申请和分配container的过程
- MOTS攻击之TCP攻击
- php概述
- php教程
- php环境搭建
- PHP书写格式
- php变量
- php常量
- PHP注释
- php数组
- php字符串 string
- PHP整型 integer
- PHP浮点型 float
- php布尔型
- php数据类型之数组
- php数据类型之对象
- php数据类型之null
- php数据类型之间的转换
- php运算符
- php表达式
- PHP循环控制
- PHP流程控制
- php函数
- php全局变量
- PHP魔术变量
- php命名空间
- php 日期
- PHP包含文件
- php文件
- PHP 文件上传
- php Cookies
- php Sessions
- php email
- php安全email
- php错误处理
- PHP异常处理
- php过滤器
- PHP 高级过滤器
- php json
- php 表单
- PHP MySQL 简介
- PHP 连接 MySQL
- php创建数据库
- php 创建表
- php mysq 插入数据
- PHP MySQL 插入多条数据
- PHP MySQL 预处理语句
- php mysql 读取数据
- php mysql where
- PHP MySQL Order By
- PHP MySQL Update
- PHP MySQL Delete
- php ODBC
- CI框架教程之优化验证码机制详解【验证码辅助函数】
- ThinkPHP框架整合微信支付之Native 扫码支付模式一图文详解
- PHP中str_split()函数的用法讲解
- 微信JSSDK分享功能图文实例详解
- spring-boot-route(八)整合mybatis操作数据库
- PHP扩展Swoole实现实时异步任务队列示例
- ThinkPHP框架下微信支付功能总结踩坑笔记
- spring-boot-route(九)整合JPA操作数据库
- spring-boot-route(十)多数据源切换
- spring-boot-route(十一)数据库配置信息加密
- PHP中number_format()函数的用法讲解
- php7新特性的理解和比较总结
- PHP之认识(二)关于Traits的用法详解
- 详细分析Python可变对象和不可变对象
- spring-boot-route(十二)整合redis做为缓存