Redis实践操作之—— keyspace notification(键空间通知)
源码地址:https://github.com/Tinywan/PHP_Experience
一、需求分析:
- 设置了生存时间的Key,在过期时能不能有所提示?
- 如果能对过期Key有个监听,如何对过期Key进行一个回调处理?
- 如何使用 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 >& &
查看jobs进程ID:[ jobs -l ]命令
www@iZ232eoxo41Z:~/tinywan $ jobs -l
[]- Stopped (tty output) sudo nohup nohupRedisNotify.php > /dev/null >&
[]+ Stopped (tty output) sudo nohup nohupRedisNotify.php > /dev/null >&
linux kill进程杀不掉(解决办法):
kill - PID
www@iZ232eoxo41Z:~/tinywan $ sudo kill -
[]+ Killed sudo nohup nohupRedisNotify.php > /dev/null >&
如果是以后运行的后台程序的,命令【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 0.0 0.2 ? S : : sudo nohup php ./nohupRedisNotify.php
root 0.0 1.5 ? S : : php ./nohupRedisNotify.php
www 0.0 0.0 pts/ S+ : : grep --color=auto nohup
经验分享环节:
1、今天在Linux服务器执行一个php脚本nohup.php 挂起一个Redis订阅事件的时候,发现每次都不会调用API接口传递参数。但是直接执行的话(php nohup.php)的时候是 可以调用接口的,经过检查发现是权限的问题:
分析:当前登录用户为www用户:但是启动脚本的时候确实这样的(Root身份执行):(可以在命令行直接执行,打印过期的事件key,并且输出可以作为调试哦!)
sudo nohup php nohupRedisNotify.php > /dev/null >& &
修改后的:
nohup php nohupRedisNotify.php > /dev/null >& &
这样的话就直接可以回调自己写的API接口啦!
Redis实践操作之—— keyspace notification(键空间通知)的更多相关文章
- 利用Redis keyspace notification(键空间通知)实现过期提醒
一.序言: 本文所说的定时任务或者说计划任务并不是很多人想象中的那样,比如说每天凌晨三点自动运行起来跑一个脚本.这种都已经烂大街了,随便一个 Crontab 就能搞定了. 这里所说的定时任务可以说是计 ...
- redis过期回调以及键空间通知
背景 最近需要涉及一个定时通知的业务,之前的办法是采用定时任务,每秒查询一次.后来了解到Redis的键空间通知机制,其中的过期通知,和业务非常贴合. 键空间通知 下面是Redis中文文档的介绍 键空间 ...
- python中的Redis键空间通知(过期回调)
介绍 Redis是一个内存数据结构存储库,用于缓存,高速数据摄取,处理消息队列,分布式锁定等等. 使用Redis优于其他内存存储的优点是Redis提供持久性和数据结构,如列表,集合,有序集和散列. 在 ...
- redis键空间通知(keyspace notification)
一.需求 在redis中,设置好key和生存时间之后,希望key过期被删除时能够及时的发送一个通知告诉我key,以便我做后续的一些操作. 二.环境 系统:windows10 php:7.1 redis ...
- Redis键空间通知(keyspace notification),事件订阅
Redis键空间通知(keyspace notification),事件订阅 应用场景:有效期优惠券.24小时内支付.下单有效事件等等. 功能概览 键空间通知使得客户端可以通过订阅频道或模式, ...
- Redis源码解析:09redis数据库实现(键值对操作、键超时功能、键空间通知)
本章对Redis服务器的数据库实现进行介绍,说明Redis数据库相关操作的实现,包括数据库中键值对的添加.删除.查看.更新等操作的实现:客户端切换数据库的实现:键超时相关功能的实现.键空间事件通知等. ...
- Redis 键空间通知
[Redis 键空间通知] 键空间通知使得客户端可以通过订阅频道或模式, 来接收那些以某种方式改动了 Redis 数据集的事件. 以下是一些键空间通知发送的事件的例子: 所有修改键的命令. 所有接收到 ...
- 10Redis键空间通知(keyspace notifications)
Redis的键空间通知(keyspace notifications)功能是自2.8.0版本开始加入的,客户端可以通过订阅/发布(Pub/Sub)机制,接收那些以某种方式改变了Redis数据空间的事件 ...
- redis中键空间通知
通过redis的键空间通知,当redis删除过期key的时候,及时更新mongodb数据库中user的状态 var Redis = require('ioredis'); var redis = ne ...
随机推荐
- 在Linux下搭建Git服务器的方法是什么样?
第一步 安装git:可以通过命令的方式快速安装,不同的linux的安装方法可能不一样,我的是采用的yum方法.ubuntu可以用apt-get命令.sudo yum install git 第二步 添 ...
- 多拉A梦——日语歌词
こんなこといいな できたらいいな 这件事真好啊 能够做到的话就好啦 あんな梦(ゆめ) こんな梦(ゆめ) いっぱいあるけど 那样的梦想 这样的梦想 我还有好多哪 みんなみんなみんな かなえてくれる 大家 ...
- PullToRefresh 下拉刷新的样式修改
资源文件结构图, 先看看下拉刷新头的布局, <?xml version="1.0" encoding="utf-8"?> <merge xml ...
- shareSDK集成步骤
按下面目录结构吧sdk的目录文件拷贝到自己的工程中 针对各个平台的分享格式,整理成了一个工具类,不同的平台分享的参数http://wiki.mob.com/不同平台分享内容的详细说明/ package ...
- css缩写
颜色: 16进制的色彩值为六位数,如果每两位的值相同,可以缩写一半. 如:#000000=#000: #223344=#234: 盒子的尺寸: 如margin:value; 一个值表示所有边,两个值表 ...
- js命名空间笔记
在量比较大或者多人编写的情况下,命名冲突就很有可能发生,同一个页面引用了两个命名相同功能不同的文件,调用的时候就会出问题.因此使用JS命名空间很重要. 1.采用字面量方法创建命名空间: var a={ ...
- 每日一九度之 题目1033:继续xxx定律
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:5502 解决:1351 题目描述: 当n为3时,我们在验证xxx定律的过程中会得到一个序列,3,5,8,4,2,1,将3称为关键数, ...
- 标准类型String(学习中)
1.读取string对象 #include<iostream> #include<cstring> using namespace std; int main() { stri ...
- 字符编码的过滤器Filter(即输入的汉字,能在页面上正常显示,不会出现乱码)
自定义抽象的 HttpFilter类, 实现自 Filter 接口 package com.lanqiao.javaweb; import java.io.IOException; import ja ...
- gulp 建立一个简单的自动化
前端项目需要的功能: 1.图片(压缩图片支持jpg.png.gif) 2.样式 (支持sass 同时支持合并.压缩.重命名) 3.javascript (检查.合并.压缩.重命名) 4.html (压 ...