當前位置: 妍妍網 > 碼農

Redis 鍵空間通知 Keyspace Notification 事件訂閱

2024-06-27碼農

概述

本文所說的定時任務或者說計劃任務並不是很多人想象中的那樣,比如說每天淩晨三點自動執行起來跑一個指令碼。這種都已經爛大街了,隨便一個 Crontab 就能搞定了。

這裏所說的定時任務可以說是計時器任務,比如說使用者觸發了某個動作,那麽從這個點開始過二十四小時我們要對這個動作做點什麽。那麽如果有 1000 個使用者觸發了這個動作,就會有 1000 個定時任務。於是這就不是 Cron 範疇裏面的內容了。

舉個最簡單的例子,一個使用者推薦了另一個使用者,我們定一個二十四小時之後的任務,看看被推薦的使用者有沒有來註冊,如果沒註冊就給他搞一條簡訊過去。

功能概覽

鍵空間通知使得客戶端可以透過訂閱頻道或模式, 來接收那些以某種方式改動了 Redis 數據集的事件。事件透過 Redis 的訂閱與釋出功能(pub/sub)來進行分發, 因此所有支持訂閱與釋出功能的客戶端都可以在無須做任何修改的情況下, 直接使用鍵空間通知功能。

鍵空間訊息

在 Redis 的 2.8.0 版本之後,其推出了一個新的特性——鍵空間訊息(Redis Keyspace Notifications),它配合 2.0.0 版本之後的 SUBSCRIBE 就能完成這個定時任務的操作了,不過定時的單位是秒。

Publish / Subscribe

Redis 在 2.0.0 之後推出了 Pub / Sub 的指令,大致就是說一邊給 Redis 的特定頻道發送訊息,另一邊從 Redis 的特定頻道取值——形成了一個簡易的訊息佇列。

Redis Keyspace Notifications

在 Redis 裏面有一些事件,比如鍵到期、鍵被刪除等。然後我們可以透過配置一些東西來讓 Redis 一旦觸發這些事件的時候就往特定的 Channel 推一條訊息。

大致的流程就是我們給 Redis 的某一個 db 設定過期事件,使其鍵一旦過期就會往特定頻道推訊息,我在自己的客戶端這邊就一直消費這個頻道就好了。

以後一來一條定時任務,我們就把這個任務狀態壓縮成一個鍵,並且過期時間為距這個任務執行的時間差。那麽當鍵一旦到期,就到了任務該執行的時間,Redis 自然會把過期訊息推去,我們的客戶端就能接收到了。這樣一來就起到了定時任務的作用。

配置

因為開啟鍵空間通知功能需要消耗一些 CPU , 所以在預設配置下, 該功能處於關閉狀態。可以透過修改 redis.conf 檔, 或者直接使用 CONFIG SET 命令來開啟或關閉鍵空間通知功能。

  • notify-keyspace-events 選項的參數為空字串時,功能關閉。

  • 當參數不是空字串時,功能開啟。

  • notify-keyspace-events 的參數可以是以下字元的任意組合, 它指定了伺服器該發送哪些型別的通知

    輸入的參數中至少要有一個 K 或者 E , 否則的話, 不管其余的參數是什麽, 都不會有任何通知被分發。舉個例子, 如果只想訂閱鍵空間中和列表相關的通知, 那麽參數就應該設為 Kl , 諸如此類。

    具體配置

    首先找到redis.conf配置檔,開啟檔,尋找 notify-keyspace-events ,將前面的 # 去掉即可。

    註意:這裏配置的是 notify-keyspace-events Ex 參數,即說明,當鍵過期的時候會觸發通知,如果只需要哈希命令鍵觸發通知則可以設定為 notify-keyspace-events Eh

    配置完成重新開機 redis-server 即可

    使用

    命令列

    開啟一個終端,redis-cli 進入 redis 。開始訂閱所有操作,等待接收訊息。

    127.0.0.1:6379> psubscribe __keyevent@0__:expired
    Reading messages... (press Ctrl-C to quit)
    1) "psubscribe"
    2) "__keyevent@0__:expired"
    3) (integer) 1

    再開啟一個終端,redis-cli 進入 redis,新增一個 20秒過期的鍵

    127.0.0.1:6379> SETEX username 30 Tinywan
    OK
    127.0.0.1:6379> get username
    "Tinywan"
    127.0.0.1:6379> TTL username
    (integer) 25
    127.0.0.1:6379> 

    另外一邊執行了阻塞訂閱操作後的終端,20秒過期後有如下資訊輸出:

    127.0.0.1:6379> 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) "username"

    輸出以上資訊,說明對過期Key資訊的訂閱是成功的。

    釋出訂閱截圖

    PHP語言程式碼

    Redis例項類RedisInstance

    <?php
    /**
     * @desc RedisInstance.php 描述資訊
     * @author Tinywan(ShaoBo Wan)
     * @date 2024/6/26 21:36
     */

    declare(strict_types=1);
    classRedisInstance
    {
    private Redis $redis;
    /**
    @param string $host
    @param int $port
    @throws RedisException
    */

    publicfunction__construct(string $host = '127.0.0.1', int $port = 6379)
    {
    $this->redis = new Redis();
    $this->redis->connect($host, $port);
    }
    /**
    @desc expire
    @param null $key
    @param int $time
    @return bool|Redis
    @throws RedisException
    @author Tinywan(ShaoBo Wan)
    */

    publicfunctionexpire($key = null, int $time = 0)
    {
    return$this->redis->expire($key, $time);
    }
    /**
    @desc psubscribe
    @param $callback
    @param array $patterns
    @throws RedisException
    @author Tinywan(ShaoBo Wan)
    */

    publicfunctionpsubscribe($callback, array $patterns = [])
    {
    $this->redis->psubscribe($patterns, $callback);
    }
    /**
    @desc setOption
    @author Tinywan(ShaoBo Wan)
    */

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



    訂閱檔 psubscribe.php

    <?php
    /**
     * @desc psubscribe.php 描述資訊
     * @author Tinywan(ShaoBo Wan)
     * @date 2024/6/26 21:39
     */
    require '../vendor/autoload.php';
    $redis = new \RedisInstance('dnmp-redis');
    $redis->setOption();
    $redis->psubscribe(function ($redis$pattern$channel$msg){
    echo'Pattern:' . $pattern .PHP_EOL;
    echo'Channel:' . $channel .PHP_EOL;
    echo'Message:' . $msg .PHP_EOL;
    }, ['__keyevent@0__:expired']);

    執行 psubscribe.php 觀察訂閱狀態

    # php psubscribe.php
    Pattern:__keyevent@0__:expired
    Channel:__keyevent@0__:expired
    Message:username
    Pattern:__keyevent@0__:expired
    Channel:__keyevent@0__:expired
    Message:username2
    Pattern:__keyevent@0__:expired
    Channel:__keyevent@0__:expired
    Message:username3

    釋出事件

    127.0.0.1:6379> SETEX username 3 Tinywan
    OK
    127.0.0.1:6379> SETEX username2 5 Tinywan
    OK
    127.0.0.1:6379> SETEX username3 5 Tinywan

    小結結

    透過以上步驟,成功地實作了Redis鍵空間通知使用。首先配置Redis伺服器,開啟鍵空間通知功能,然後透過命令列和編寫客戶端程式碼來接收並處理通知。這個功能可以幫助我們即時地獲取資料庫操作的變化,非常適用於需要即時更新數據的應用程式。