首页 > 数据库 >Redis实践操作之—— keyspace notification(键空间通知)

Redis实践操作之—— keyspace notification(键空间通知)

时间:2023-04-05 20:37:00浏览次数:38  
标签:__ redis 过期 notification Redis keyspace nohup php


源码地址:https://github.com/Tinywan/PHP_Experience


一、需求分析:

  1. 设置了生存时间的Key,在过期时能不能有所提示?
  2. 如果能对过期Key有个监听,如何对过期Key进行一个回调处理?
  3. 如何使用 Redis 来实现定时任务?

二、序言:

       本文所说的定时任务或者说计划任务并不是很多人想象中的那样,比如说每天凌晨三点自动运行起来跑一个脚本。这种都已经烂大街了,随便一个 Crontab 就能搞定了。

       这里所说的定时任务可以说是计时器任务,比如说用户触发了某个动作,那么从这个点开始过二十四小时我们要对这个动作做点什么。那么如果有 1000 个用户触发了这个动作,就会有 1000 个定时任务。于是这就不是 Cron 范畴里面的内容了。

       举个最简单的例子,一个用户推荐了另一个用户,我们定一个二十四小时之后的任务,看看被推荐的用户有没有来注册,如果没注册就给他搞一条短信过去。

三、Redis介绍

       在 Redis 的 2.8.0 版本之后,其推出了一个新的特性——键空间消息(Redis Keyspace Notifications),它配合 2.0.0 版本之后的 SUBSCRIBE 就能完成这个定时任务

的操作了,不过定时的单位是秒。

(1)Publish / Subscribe 

     Redis 在 2.0.0 之后推出了 Pub / Sub 的指令,大致就是说一边给 Redis 的特定频道发送消息,另一边从 Redis 的特定频道取值——形成了一个简易的消息队列。

(2)Redis Keyspace Notifications

      在 Redis 里面有一些事件,比如键到期、键被删除等。然后我们可以通过配置一些东西来让 Redis 一旦触发这些事件的时候就往特定的 Channel 推一条消息。

     大致的流程就是我们给 Redis 的某一个 db 设置过期事件,使其键一旦过期就会往特定频道推消息,我在自己的客户端这边就一直消费这个频道就好了。

     以后一来一条定时任务,我们就把这个任务状态压缩成一个键,并且过期时间为距这个任务执行的时间差。那么当键一旦到期,就到了任务该执行的时间,Redis 自然会把过期消息推去,我们的客户端就能接收到了。这样一来就起到了定时任务的作用。

 四、Key过期事件的Redis配置

     这里需要配置 notify-keyspace-events 的参数为 “Ex”。x 代表了过期事件。notify-keyspace-events "Ex" 保存配置后,重启Redis服务,使配置生效。
重启Reids服务器:



root@iZ23s8agtagZ:/etc/redis# service redis-server restart redis.conf
Stopping redis-server: redis-server.
Starting redis-server: redis-server.



添加过期事件订阅 开启一个终端,redis-cli 进入 redis 。开始订阅所有操作,等待接收消息。



tinywan@iZ23a7607jaZ:~$ redis-cli -h 127.0.01.4 -p 63789
127.0.0.1:63789> psubscribe __keyevent@0__:expired
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyevent@0__:expired"
3) (integer) 1



再开启一个终端,redis-cli 进入 redis,新增一个 20秒过期的键:



1270.01.1.1:63789> SETEX coolName 123 20
OK
121.41.188.109:63789> get coolName
"20"
121.41.188.109:63789> ttl coolName
(integer) 104



另外一边执行了阻塞订阅操作后的终端,20秒过期后有如下信息输出:



121.141.188.209:63789> 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"
4) "coolName"



说明:说明对过期Key信息的订阅是成功的。

 五、PHPREDIS实现订阅Keyspace notification

 Redis实例化类:(RedisInstance.class.php)



<?php

class RedisInstance
{
    private $redis;

    public function __construct($host = '121.41.88.209', $port = 63789)
    {
        $this->redis = new Redis();
        $this->redis->connect($host, $port);
    }

    public function expire($key = null, $time = 0)
    {
        return $this->redis->expire($key, $time);
    }

    public function psubscribe($patterns = array(), $callback)
    {
        $this->redis->psubscribe($patterns, $callback);
    }

    public function setOption()
    {
        $this->redis->setOption(\Redis::OPT_READ_TIMEOUT,-1);
    }

}



过期事件的订阅:(psubscribe.php)



<?php
require_once './RedisInstance.class.php';
$redis = new \RedisInstance();
// 解决Redis客户端订阅时候超时情况
$redis->setOption();
$redis->psubscribe(array('__keyevent@0__:expired'), 'psCallback');
// 回调函数,这里写处理逻辑
function psCallback($redis, $pattern, $chan, $msg)
{
    echo "Pattern: $pattern\n";
    echo "Channel: $chan\n";
    echo "Payload: $msg\n\n";
}



说明:psCallback 函数为订阅事件后的回调函数。$redis, $pattern, $chan, $msg 四个参数为回调时返回的参数。 详细说明见下面 Redis 官方文档对 psubscribe 的说明。

因为订阅事件启动后是阻塞执行的,所以我们尝试在终端执行 psubscribe.php 这个脚本。



D:\wamp\www\redistest>php psubscribe.php



设置一个过期事件:



121.41.188.109:63789> SETEX username123 20



20秒过期时间到时,另外一边执行了脚本被阻塞的终端,有如下信息输出订阅结果:



D:\wamp\www\redistest>php psubscribe.php
Pattern: __keyevent@0__:expired
Channel: __keyevent@0__:expired
Payload: username123



以上PHP操作Reids是成功的

 六、使监听后台始终运行(订阅)

     有个问题 做到这一步,利用 phpredis 扩展,成功在代码里实现对过期 Key 的监听,并在 psCallback()里进行回调处理。 开头提出的两个需求已经实现。 可是这里有个问题:redis 在执行完订阅操作后,终端进入阻塞状态,需要一直挂在那。且此订阅脚本需要人为在命令行执行,不符合实际需求。

    实际上,我们对过期监听回调的需求,是希望它像守护进程一样,在后台运行,当有过期事件的消息时,触发回调函数。 使监听后台始终运行 希望像守护进程一样在后台一样,

我是这样实现的。

    Linux中有一个nohup命令。功能就是不挂断地运行命令。 同时nohup把脚本程序的所有输出,都放到当前目录的nohup.out文件中,如果文件不可写,则放到<用户主目录>/nohup.out 文件中。那么有了这个命令以后,不管我们终端窗口是否关闭,都能够让我们的php脚本一直运行。

编写PHP脚本文件:



<?php
#! /usr/local/php/bin/php
require_once './redis.class.php';
$redis = new MyRedis();
$redis->setOption();
$redis->psubscribe(array('__keyevent@0__:expired'), 'psCallback');
// 回调函数,这里写处理逻辑
function psCallback($redis, $pattern, $chan, $msg)
{
    echo "Pattern: $pattern\n";
    echo "Channel: $chan\n";
    echo "Payload: $msg\n\n";
}



注意:不过我们在开头,需要申明 php 编译器的路径:#! /usr/local/php/bin/php 。 这是执行 php 脚本所必须的。

然后,nohup 不挂起执行 nohupRedisNotify.php,注意 末尾的 &



[root@chokingwin HiGirl]# nohup ./nohupRedisNotify.php & [1] 4456 nohup: ignoring input and appending output to `nohup.out'



确认一下脚本是否已在后台运行。查看进程结果如下: 



[root@chokingwin HiGirl]# ps PID TTY TIME CMD 3943 pts/2 00:00:00 bash 4456 pts/2 00:00:00 nohupRedisNotif 4480 pts/2 00:00:00



说明:脚本确实已经在 4456 号进程上跑起来。 

最后在查看下nohup.out cat 一下 nohuo.out,看下是否有过期输出:



[root@chokingwin HiGirl]# cat nohup.out [root@chokingwin HiGirl]#



并没有。我们还是老样子,新增一个10秒过期的的键 name。10秒后,我们再 cat 一次。 



[root@chokingwin HiGirl]# cat nohup.out Pattern: __keyevent@0__:expired Channel: __keyevent@0__:expired Payload: name



说明监听过期事件并回调成功。

nohup命令:记录详情:

nohup没有输出的情况:



sudo nohup nohupRedisNotify.php > /dev/null 2>&1 &



查看jobs进程ID:[ jobs -l ]命令



www@iZ232eoxo41Z:~/tinywan $ jobs -l
[1]-  1365 Stopped (tty output)    sudo nohup nohupRedisNotify.php > /dev/null 2>&1
[2]+  1370 Stopped (tty output)    sudo nohup nohupRedisNotify.php > /dev/null 2>&1



linux kill进程杀不掉(解决办法):



kill -9 PID



www@iZ232eoxo41Z:~/tinywan $ sudo kill -9 1370
[2]+  Killed                  sudo nohup nohupRedisNotify.php > /dev/null 2>&1



 如果是以后运行的后台程序的,命令【jobs -l】是没办法察觉任务以及PID的,这时候可以借助:ps aux | grep nohup 筛选哦,在使用 kill PID 铲除即可

在这里是www用户已root运行的任务,用jobs -l

jobs -l



www@iZ232eoxo41Z:~ $ jobs -l
www@iZ232eoxo41Z:~ $



ps aux | grep nohup 



www@iZ232eoxo41Z:~ $ ps aux | grep nohup
root     18448  0.0  0.2  65160  2088 ?        S    14:44   0:00 sudo nohup php ./nohupRedisNotify.php
root     18449  0.0  1.5 283368 15956 ?        S    14:44   0:00 php ./nohupRedisNotify.php
www      18605  0.0  0.0  11740   928 pts/1    S+   14:50   0:00 grep --color=auto nohup



 经验分享环节:

1、今天在Linux服务器执行一个php脚本nohup.php 挂起一个Redis订阅事件的时候,发现每次都不会调用API接口传递参数。但是直接执行的话(php nohup.php)的时候是 可以调用接口的,经过检查发现是权限的问题:

分析:当前登录用户为www用户:但是启动脚本的时候确实这样的(Root身份执行):(可以在命令行直接执行,打印过期的事件key,并且输出可以作为调试哦!)



sudo nohup php nohupRedisNotify.php > /dev/null 2>&1 &



修改后的:



nohup php nohupRedisNotify.php > /dev/null 2>&1 &



这样的话就直接可以回调自己写的API接口啦!

 









标签:__,redis,过期,notification,Redis,keyspace,nohup,php
From: https://blog.51cto.com/tinywan/6171612

相关文章

  • Redis 在消息队列中的应用
    1.Redis的List数据类型1.1List数据类型的特点List列表是Redis提供的一种重要的数据类型。它是由若干个字符串元素组成的集合,并且每个字符串元素都是按照插入顺序排序的。也可以将列表理解为多个字符串组成的一个集合对象,并按照链表(LinkList)的插入顺序排序。在读......
  • redis主从同步、哨兵自动故障迁移具体过程解析
    前言本问阐述Redis主从同步、哨兵监控和故障迁移的过程,并通过各个服务日志加以验证。准备工作主从和哨兵安装见安装过程。为了方便通过日志观察同步过程,你可以在每个Redis和Sentinel配置文件中修改下面参数,实现前台运行并将日志输出到控制台。#这两个配置是为了实......
  • RedisTemplate Pipeline 封装和使用,优化Redis操作
    前言公司游戏项目服务端运行过程中,依赖Redis保存游戏过程数据。与客户端一次交互会对Redis进行大量的操作,在内网开发环境下测试,一次操作开销在1-2ms,建立连接和网络开销累积在一起大大降低了响应速度,这种场景下可以使用RedisPipeline进行优化。RedisPipelineRedis服......
  • Redis布隆过滤器的原理和安装使用
    前言本文讲述布隆过滤器(RedisBloom)的基本原理和安装使用。RedisBloom是什么?RedisBloom是Redis中过滤器模块,可以用来判断值是否存在,常用来解决缓存穿透问题。查询数据时,先用RedisBloom判断数据是否存在,不存在则直接返回,存在则从缓存/数据库获取后返回。比如查询接......
  • Redis 在排行榜中的应用
    1.Redis的SortedSet数据类型1.1SortedSet数据类型的特点SortedSet有序集合是Redis提供的一种重要的数据类型。它是由不重复且有序的字符串元素组成的,而且每个元素都会关联一个double类型的分数,通过该分数来为集合中的成员进行从小到大的排序。SortedSet的......
  • Redis持久化RDB和AOF原理解析、使用和优缺点对比
    前言本文讲述Redis两种持久化方式RDB和AOF优缺点以及原理。为何需要持久化?Redis是基于内存操作的,进程终止、服务器宕机后内存数据会丢失,但是在很多使用场景中我们希望数据不丢失,服务重启之后数据还能恢复到停机前的状态,特别是使用Redis做数据库的情况。Redis持久化......
  • Redis 在身份认证中的应用
    1.Redis在Session共享问题中的应用传统Session-Cookeis身份认证方法中,一个Session只保存在一台服务器上,适合域单体应用。随着项目规模的增加,项目的架构也不断向微服务分布式集群演进,传统的Session-Cookie方式在集群环境下就不能很好的工作了,这时就产生了Session共......
  • Redis 缓存机制
    1.Redis缓存缓存(cache),原始意义是指访问速度比一般随机存取存储器(RAM)快的一种高速存储器,通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。Redis因读写性能较高,它非常适合作为存贮数据......
  • 在Linux部署Redis主从和哨兵集群实现高可用
    前言本文主要讲述在Linux系统中配置和部署Redis主从集群和哨兵,实现高可用和自动故障迁移。准备工作参考Redis单机部署安装3个Redis服务作一主二从,本文准备了6380(主)、6381(从)和6382(从)。参考Redis单机部署安装3个Redis服务作哨兵集群,本文准备了26380......
  • 在Linux部署Redis Cluster集群
    前言本文讲述在Linux系统部署RedisCluster实现数据分片的具体步骤。请参考Redis单机部署下载编译。RedisCluster是什么?RedisCluster是官方提供的一种用数据分片来实现横向扩容的解决方案,由一个或多个Redis服务组成一个无主集群。对Key使用哈希算法将数据分散......