之前的订单过期自动删除,是通过队列延时任务执行的,这次尝试通过Redis的 订阅过期事件,理论上比队列任务效率更高,当然如果你队列通过swoole常驻内存实现,那当我没说。
还是基于laravel环境写的。不过这个不是必须的。
1、修改服务器redis配置redis.conf
notify-keyspace-events Ex
2、新增一个redis配置用于存储需要监听的数据
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'publisher' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '2'),
'read_write_timeout' => 0,
'prefix'=>''
],
],
这里需要注意的prefix 前缀配置,因为我们系统是比较大的集群项目,所以redis存储都有前缀
但是 数据过期Redis自动发布的通道,不经过laravel系统,不会做前缀处理;但是laravel的监听会自动加前缀,导致无法监听到。如下图:
所以单独给publisher 配置了 prefix 为空。
3、写一个命令实现过期数据的订阅(监听)
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
class Test extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'test';
/**
* The console command description.
*
* @var string
*/
protected $description = '在5分钟后取消未付款订单。';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
ini_set('default_socket_timeout', -1);
$redis=Redis::connection('publisher');//创建新的实例
$redis->psubscribe(['__keyevent@*__:expired'], function ($message, $channel) {
echo $channel.PHP_EOL;//订阅的频道
echo $message.PHP_EOL;//过期的key
// 这里就可以根据监听到的过期键值来删除数据库的无用数据了
});
}
}
如果不配置 ini_set('default_socket_timeout', -1); 而你用的是php的redis拓展,会发现命令执行到一定时间会报下面这个错误。这是因为php的redis拓展底层走的php的socket,会受到php配置文件中的socket连接时长限制。
RedisException
read error on connection to 127.0.0.1:****
4、最后把这个执行命令放到守护进程中,做常驻进程执行即可。
ps: Redis 不光可以监听事件,还可以监听特定键值,参考