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 通知。
配置具體說明:
- 首先找到redis.conf配置文件,打開文件,查找notify-keyspace-events,將前面的#去掉便可。注意:這裏配置的是notify-keyspace-events的Ex參數,即說明,當鍵過時的時候會觸發通知,若是隻須要哈希命令鍵觸發通知則能夠設置爲notify-keyspace-events Eh。
- 重啓redis-server。
- 配置完成。
代碼說明:
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. 代碼準備完成。
進行測試
- 運行psubscribe.php。
- 運行 index.php。
- 觀察訂閱狀態。以下。
- 事件觸發通知成功。
完成
參考資料:http://redisdoc.com/topic/notification.htmlphp