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.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章