redis鍵空間通知使用實現

導語

最近在開發一個定時活動,而且活動是多個場次的。這個是後就需要在活動開始的時候推送信息給客戶端,結束的時候也要推送一次。簡單的設計方案就是將配置緩存在redis,然後每隔一秒就輪詢reids,獲取配置信息,然後判斷是不是到活動開始或者結束的時間點,然後推送給客戶端。

但是,這裡會有一個問題,如果沒有到活動開始或結束的時間點,這裡會造成很多無用的輪詢操作。這個操作不但增大瞭對這個key的訪問量,同時也會占用cpu,降低機器性能。

redis在2.8.0版本提供瞭一個鍵空間通知功能機制,對於這個功能的詳細描述,可以查閱官方文檔。簡單總結就是,客戶端可以訂閱一個key,當這個可以發生改變時,redis會通知到已經訂閱的客戶端。

實現

這個實現也很簡單,我們可以通過一個demo來看看如何使用這個機制。

package main

import (
   "context"
   "fmt"
   "github.com/go-redis/redis/v8"
   "time"
)

var redisCli *redis.Client

func init() {
   // 連接redis
   redisCli = redis.NewClient(&redis.Options{
      Addr:     "127.0.0.1:6379",
      Password: "redis123",
   })
}

/*
 * redis key 過期自動通知
 */
func SetExpireEvent() {
   // 設置一個鍵,並且3秒鐘之後過期
   redisCli.Set(context.Background(), "test_expire_event_notify", "測試鍵值過期通知", 3*time.Second)
}

func SubExpireEvent() {
   // 訂閱key過期事件
   sub := redisCli2.Subscribe(context.Background(), "__keyevent@0__:expired")
   
   // 這裡通過一個for循環監聽redis-server發來的消息。
   // 當客戶端接收到redis-server發送的事件通知時,
   // 客戶端會通過一個channel告知我們。我們再根據
   // msg的channel字段來判斷是不是我們期望收到的消息,
   // 然後再進行業務處理。
   for {
      msg := <-sub.Channel()
      fmt.Println("Channel ", msg.Channel)
      fmt.Println("pattern ", msg.Pattern)
      fmt.Println("pattern ", msg.Payload)
      fmt.Println("PayloadSlice ", msg.PayloadSlice)
   }
}

func main() {
   SetExpireEvent()
   go SubExpireEvent()
   
   // 這裡sleep是為瞭防止main方法直接推出
   time.Sleep(10 * time.Second)
}

代碼結果輸出如下:

上面代碼實現邏輯很簡單,核心邏輯就是訂閱__keyevent@0__:expired這個事件,然後一個循環等待事件的通知。值得註意的是,要啟用這個特性需要修改配置文件,啟用notify-keyspace-events這個配置,可以參考配置文件中的註釋對不同事件進行啟用。

在業務中使用

回到開始提及的業務場景,如何在這種場景中使用redis的機制呢?其實很簡單,當活動配置到數據庫之後,會有一個更新緩存的步驟。在將數據設置在活動緩存時,隻要我們計算當前時間到活動開始/結束這個時間差,將這個差作為鍵的過期時間。

例如,活動id1的開始時間為t0, 結束時間為t2, 當前時間為t。這個時候就可以這麼設置:

// 活動開始的key設置
redisCli.Set(context.Background(), "id1:start", "活動開始瞭", t0 - t)
// 活動結束結束的key設置
redisCli.Set(context.Background(), "id1:start", "活動開始瞭", t1 - t)

通過這麼設置,當活動開啟/結束就可以接收到相應的通知瞭。

總結

這種方案其實可以完全滿足文中的需求場景,但是這種方案其實也存在一些問題。其實這些問題在redis文檔中也有相應說明。

  • 第一,redis-server在推送這個事件通知時,隻要訂閱瞭這個事件的客戶端端都會收到這個消息。通常,我們的業務都是跑在多個結點中,所以這個時候就要根據場景看要不要進行業務的原子操作。
  • 第二,redis-server隻會推送一次這個通知。假如說在redis-server推送這個通知時,結點掛瞭或者由於其他異常情況沒有收到消息,redis-server不會再重新推送。
  • 第三,通知可能會延遲。由於redis實現機制,對於過期的鍵,會有兩種機制進行處理,一種是當命令訪問鍵時,發現鍵已過期。另一種是通過後臺系統在後臺逐步查找過期的鍵,以便能夠收集那些從未被訪問的鍵。所以會有出現延遲的可能。

本文介紹瞭使用redis的鍵空間通知機制來實現瞭一種業務場景,當然這種方式並不是最好的,還有其他方式來實現。在實際開發中會有很多的因素要考慮,而且實現方式也是多種多樣,這個就需要我們分析每一種方案的利弊,然後進行抉擇。

到此這篇關於redis鍵空間通知使用實現的文章就介紹到這瞭,更多相關redis鍵空間通知 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: