Redis键空间通知(keyspace notification),事件订阅

2019年11月18日 阅读数:108
这篇文章主要向大家介绍Redis键空间通知(keyspace notification),事件订阅,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。
 

Redis键空间通知(keyspace notification),事件订阅

 

应用场景:有效期优惠券、24小时内支付、下单有效事件等等。

功能概览

键空间通知使得客户端能够经过订阅频道或模式, 来接收那些以某种方式改动了 Redis 数据集的事件。
如下是一些键空间通知发送的事件的例子:
  • 全部修改键的命令。
  • 全部接收到 LPUSH key value [value …] 命令的键。
  • 0 号数据库中全部已过时的键。
事件经过 Redis 的订阅与发布功能(pub/sub)来进行分发, 所以全部支持订阅与发布功能的客户端均可以在无须作任何修改的状况下, 直接使用键空间通知功能。
由于 Redis 目前的订阅与发布功能采起的是发送即忘(fire and forget)策略, 因此若是你的程序须要可靠事件通知(reliable notification of events), 那么目前的键空间通知可能并不适合你: 当订阅事件的客户端断线时, 它会丢失全部在断线期间分发给它的事件。
将来将会支持更可靠的事件分发, 这种支持可能会经过让订阅与发布功能自己变得更可靠来实现, 也可能会在 Lua 脚本中对消息(message)的订阅与发布进行监听, 从而实现相似将事件推入到列表这样的操做。
事件的类型
对于每一个修改数据库的操做,键空间通知都会发送两种不一样类型的事件。
好比说,对 0 号数据库的键 mykey 执行 DEL key [key …] 命令时, 系统将分发两条消息, 至关于执行如下两个 PUBLISH channel message 命令:
PUBLISH __keyspace@0__:mykey del PUBLISH __keyevent@0__:del mykey
订阅第一个频道 __keyspace@0__:mykey 能够接收 0 号数据库中全部修改键 mykey 的事件, 而订阅第二个频道 __keyevent@0__:del 则能够接收 0 号数据库中全部执行 del 命令的键。
以 keyspace 为前缀的频道被称为键空间通知(key-space notification), 而以 keyevent 为前缀的频道则被称为键事件通知(key-event notification)。
当 del mykey 命令执行时:
  • 键空间频道的订阅者将接收到被执行的事件的名字,在这个例子中,就是 del 。
  • 键事件频道的订阅者将接收到被执行事件的键的名字,在这个例子中,就是 mykey 。

配置

由于开启键空间通知功能须要消耗一些 CPU , 因此在默认配置下, 该功能处于关闭状态。
能够经过修改 redis.conf 文件, 或者直接使用 CONFIG SET 命令来开启或关闭键空间通知功能:
  • 当 notify-keyspace-events 选项的参数为空字符串时,功能关闭。
  • 另外一方面,当参数不是空字符串时,功能开启。
 
notify-keyspace-events 的参数能够是如下字符的任意组合, 它指定了服务器该发送哪些类型的通知:
字符
发送的通知
K
键空间通知,全部通知以 __keyspace@<db>__ 为前缀
E
键事件通知,全部通知以 __keyevent@<db>__ 为前缀
g
DEL 、 EXPIRE 、 RENAME 等类型无关的通用命令的通知
$
字符串命令的通知
l
列表命令的通知
s
集合命令的通知
h
哈希命令的通知
z
有序集合命令的通知
x
过时事件:每当有过时键被删除时发送
e
驱逐(evict)事件:每当有键由于 maxmemory 政策而被删除时发送
A
参数 g$lshzxe 的别名
输入的参数中至少要有一个 K 或者 E , 不然的话, 无论其他的参数是什么, 都不会有任何通知被分发。
举个例子, 若是只想订阅键空间中和列表相关的通知, 那么参数就应该设为 Kl , 诸如此类。
将参数设为字符串 "AKE" 表示发送全部类型的通知。
 

命令产生的通知

如下列表记录了不一样命令所产生的不一样通知:
  • DEL key [key …] 命令为每一个被删除的键产生一个 del 通知。
  • RENAME key newkey 产生两个通知:为来源键(source key)产生一个 rename_from 通知,并为目标键(destination key)产生一个 rename_to 通知。
  • EXPIRE key seconds 和 EXPIREAT key timestamp 在键被正确设置过时时间时产生一个 expire 通知。当 EXPIREAT key timestamp 设置的时间已通过期,或者 EXPIRE key seconds传入的时间为负数值时,键被删除,并产生一个 del 通知。
  • SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC | DESC] [ALPHA] [STORE destination] 在命令带有 STORE 参数时产生一个 sortstore 事件。若是 STORE 指示的用于保存排序结果的键已经存在,那么程序还会发送一个 del 事件。
  • SET key value [EX seconds] [PX milliseconds] [NX|XX] 以及它的全部变种(SETEX key seconds value 、 SETNX key value 和 GETSET key value)都产生 set 通知。其中 SETEX key seconds value 还会产生 expire 通知。
  • MSET key value [key value …] 为每一个键产生一个 set 通知。
  • SETRANGE key offset value 产生一个 setrange 通知。
  • INCR key 、 DECR key 、 INCRBY key increment 和 DECRBY key decrement 都产生 incrby通知。
  • INCRBYFLOAT key increment 产生 incrbyfloat 通知。
  • APPEND key value 产生 append 通知。
  • LPUSH key value [value …] 和 LPUSHX key value 都产生单个 lpush 通知,即便有多个输入元素时,也是如此。
  • RPUSH key value [value …] 和 RPUSHX key value 都产生单个 rpush 通知,即便有多个输入元素时,也是如此。
  • RPOP key 产生 rpop 通知。若是被弹出的元素是列表的最后一个元素,那么还会产生一个 del 通知。
  • LPOP key 产生 lpop 通知。若是被弹出的元素是列表的最后一个元素,那么还会产生一个 del 通知。
  • LINSERT key BEFORE|AFTER pivot value 产生一个 linsert 通知。
  • LSET key index value 产生一个 lset 通知。
  • LTRIM key start stop 产生一个 ltrim 通知。若是 LTRIM key start stop 执行以后,列表键被清空,那么还会产生一个 del 通知。
  • RPOPLPUSH source destination 和 BRPOPLPUSH source destination timeout 产生一个 rpop 通知,以及一个 lpush 通知。两个命令都会保证 rpop 的通知在 lpush 的通知以前分发。若是从键弹出元素以后,被弹出的列表键被清空,那么还会产生一个 del 通知。
  • HSET hash field value 、 HSETNX hash field value 和 HMSET 都只产生一个 hset 通知。
  • HINCRBY 产生一个 hincrby 通知。
  • HINCRBYFLOAT 产生一个 hincrbyfloat 通知。
  • HDEL 产生一个 hdel 通知。若是执行 HDEL 以后,哈希键被清空,那么还会产生一个 del 通知。
  • SADD key member [member …] 产生一个 sadd 通知,即便有多个输入元素时,也是如此。
  • SREM key member [member …] 产生一个 srem 通知,若是执行 SREM key member [member …] 以后,集合键被清空,那么还会产生一个 del 通知。
  • SMOVE source destination member 为来源键(source key)产生一个 srem 通知,并为目标键(destination key)产生一个 sadd 事件。
  • SPOP key 产生一个 spop 事件。若是执行 SPOP key 以后,集合键被清空,那么还会产生一个 del 通知。
  • SINTERSTORE destination key [key …] 、 SUNIONSTORE destination key [key …] 和 SDIFFSTORE destination key [key …] 分别产生 sinterstore 、 sunionostore 和 sdiffstore三种通知。若是用于保存结果的键已经存在,那么还会产生一个 del 通知。
  • ZINCRBY key increment member 产生一个 zincr 通知。(译注:非对称,请注意。)
  • ZADD key score member [[score member] [score member] …] 产生一个 zadd 通知,即便有多个输入元素时,也是如此。
  • ZREM key member [member …] 产生一个 zrem 通知,即便有多个输入元素时,也是如此。若是执行 ZREM key member [member …] 以后,有序集合键被清空,那么还会产生一个 del 通知。
  • ZREMRANGEBYSCORE key min max 产生一个 zrembyscore 通知。(译注:非对称,请注意。)若是用于保存结果的键已经存在,那么还会产生一个 del 通知。
  • ZREMRANGEBYRANK key start stop 产生一个 zrembyrank 通知。(译注:非对称,请注意。)若是用于保存结果的键已经存在,那么还会产生一个 del 通知。
  • ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] 和 ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] 分别产生 zinterstore 和 zunionstore 两种通知。若是用于保存结果的键已经存在,那么还会产生一个 del 通知。
  • 每当一个键由于过时而被删除时,产生一个 expired 通知。
  • 每当一个键由于 maxmemory 政策而被删除以回收内存时,产生一个 evicted 通知。
 
 

配置具体说明:

  1. 首先找到redis.conf配置文件,打开文件,查找notify-keyspace-events,将前面的#去掉便可。注意:这里配置的是notify-keyspace-events的Ex参数,即说明,当键过时的时候会触发通知,若是只须要哈希命令键触发通知则能够设置为notify-keyspace-events Eh。
  2. 重启redis-server。
  3. 配置完成。
 

代码说明:

1. 新建一个Redis.class.php的文件。代码以下:注意,这里使用的类名是Redis2,避免与扩展Redis类冲突。
<?php
class Redis2
{
private $redis;
 
public function __construct($host = '127.0.0.1', $port = 6379)
{
$this->redis = new Redis();
$this->redis->connect($host, $port);
}
 
public function setex($key, $time, $val)
{
return $this->redis->setex($key, $time, $val);
}
 
public function set($key, $val)
{
return $this->redis->set($key, $val);
}
 
public function get($key)
{
return $this->redis->get($key);
}
 
public function incr($key)
{
return $this->redis->incr($key);
}
 
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);
}
 
}
 
2. 新建订阅类,psubscribe.php文件,注意:这里的__keyevent@0__:incrby说明,0为须要通知的数据库,这里是当前0数据库,incrby为须要产生通知的事件设置,这里设置的是incrby。即,参考上面的命令产生通知,当INCR key 、 DECR key 、 INCRBY key increment 和 DECRBY key decrement 都产生 incrby通知,因此这里incr命令都会产生一条订阅的通知。若是是想要设置过时通知,则__keyevent@0__:incrby改成__keyevent@0__:expired。
<?php
require_once './Redis.class.php';
$redis = new \Redis2();
// 解决Redis客户端订阅时候超时状况
$redis->setOption();
$redis->psubscribe(array('__keyevent@0__:incrby'), 'keyCallback');
// 回调函数,这里写处理逻辑
function keyCallback($redis, $pattern, $chan, $msg)
{
echo "Pattern: $pattern\n";
echo "Channel: $chan\n";
echo "Payload: $msg\n\n";
//keyCallback为订阅事件后的回调函数,这里写业务处理逻辑,
//好比前面提到的商品不支付自动撤单,这里就能够根据订单id,来实现自动撤单
}
 
3. 新建测试类,index.php。
<?php
require_once './Redis.class.php';
$redis = new \Redis2();
$var = 123;
$redis->incr($var);
 
4. 代码准备完成。
 

进行测试

  1. 运行psubscribe.php。
  2. 运行 index.php。
  3. 观察订阅状态。以下。
  4. 事件触发通知成功。

完成

参考资料:http://redisdoc.com/topic/notification.htmlphp