首頁 >資料庫 >Redis >淺談redis實現即時訂閱推播的三種方法

淺談redis實現即時訂閱推播的三種方法

青灯夜游
青灯夜游轉載
2021-03-25 11:49:042878瀏覽

20w 的推送用戶,如何做到秒級並發完成?這篇文章為大家介紹redis實現即時訂閱推播的三種方法:MQ、傳統定時任務以及Redis的SortSet佇列。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

淺談redis實現即時訂閱推播的三種方法

【相關推薦:Redis影片教學

前陣子開發了公司領劵中心的項目,這個項目是以redis 以關鍵技術落地的。

先說一下領劵中心的專案吧,這個專案就類似京東 app 的領劵中心,當然圖是截取京東的,公司的就不截了。 。 。

淺談redis實現即時訂閱推播的三種方法

#其中有一個功能叫做領劵的訂閱推送。

什麼是領劵的訂閱推送?

就是用戶訂閱了該劵的推送,在可領取前的一分鐘就要把提醒訊息推送到用戶的 app 中。

本來這個訂閱功能應該是在訊息中心那邊做的,但他們說這個短時間內就做不了。所以讓我這個負責優惠劵的做了 -.-!。具體方案就是到具體的推送時間點了,coupon 系統呼叫訊息中心的推送接口,把訊息推送出去。

下們我們分析一下這個功能的業務情境。公司目前註冊用戶 6000W ,是哪家就不要打聽了。 。 。例如有一張無門檻的優惠劵下單立減 20 元,那麼搶這張劵的人就會比較多,我們保守估計 10W ,百萬級別不好說。我們初定為 20W 萬人,那麼這 20W 條推播訊息要在一分鐘推送完成!且有一個用戶是可以訂閱多張劵的。所以我們知道了這個訂閱功能的有兩個突出的困難:

  • 推送的實效性:推送慢了,用戶會抱怨沒有及時通知他們錯過了開搶時機。

  • 推送的體積大:爆款的神劵,大家都想搶!

然而推送體量又會影響到推送的實效性。這真是一個讓人頭痛的問題!

那就讓我們把問題一個個解決掉吧!

推送的實效性的問題:當用戶在領劵中心訂閱了某個劵的領取提醒後,在後台就會產生一條用戶的訂閱提醒記錄,裡面記錄了在哪個時間點給用戶發送推送訊息。所以問題變成了系統如何快速即時選出哪些要推送的記錄!

方案 1:

MQ 的延遲投遞。 MQ 雖然支援訊息的延遲投遞但尺度太大 1s 5s 10s 30s 1m,用來做精確時間點投遞不行!而用戶執行訂閱之後又取消訂閱的話,要把發出去的 MQ 訊息 delete 掉這個操作有點頭大,短時間內難以落地!而用戶可以取消之後再訂閱,這又涉及到去重的問題。所以 MQ 的方案否掉。

方案 2:

傳統定時任務。這個相對來說簡單一點,用定時任務是去 db 裡面 load 用戶的訂閱提醒記錄,從中選出目前可以推送的記錄。但有句話說得好任何脫離實際業務的設計都是耍流氓~。下面我們就分析一下傳統的定時任務到底適不適合我們的這個業務!

能否支援多機同時跑 一般不能,同一時刻只能單機跑。

儲存資料來源

一般是mysql 或其它傳統資料庫,並且是單表儲存
頻率 支援秒、分、時、天,一般不能太快
#

綜上所述我們就知道了一般傳統的定時任務有以下缺點:

1、效能瓶頸。只有一台機在處理,在大體量資料面前力不從心! 

2、實效性差。定時任務的頻率不能太高,太高會業務資料庫造成很大的壓力! 

3、單點故障。

萬一跑的那台機掛了,那整個業務不可用了 -。 - 這是一件很可怕的事!所以傳統定時任務也不太適合這個業務。 。 。  那我們是不是就束手無策了呢?其實不是的! 我們只要對傳統的定時任務做一個簡單的改造!就可以把它變成可以同時多機跑, 並且實效性可以精確到秒級,並且拒絕單點故障的定時任務集群!這其中就要藉助我們的強大的 redis 了。 

方案 3:

定時任務群集 首先我們要定義定時任務群集要解決的三個問題!

1、實效性高     

2、吞吐量大     

3、服務穩定,不能有單點故障  以下是整個定時任務叢集的架構圖。

淺談redis實現即時訂閱推播的三種方法

架構很簡單:我們把使用者的訂閱推送記錄儲存到redis 叢集的sortedSet 佇列裡面, 並且以提醒用戶提醒時間戳記作為score 值,然後在我們個每業務server 裡面一個定時器頻率是秒級,我的設定就是1s,然後經過負載平衡之後從某個佇列裡面取得要推送的使用者記錄進行推送。下面我們分析以下這個架構。    

1、效能:除去頻寬等其它因素,基本上與機器數成線性相關。機器數量越多吞吐量越大,機器數量少時的相對吞吐量就減少。    

2、實效性:提高到了秒級,效果還可以接受。    

3、單點故障?不存在的!除非 redis 叢集或所有 server 全掛了。 。 。 。 

這裡解析一下為什麼要用 redis? 

第一 redis 可以作為一個高效能的儲存 db,效能比 MySQL 好很多,並且支援持久化,穩定性好。 

第二個 redis SortedSet 佇列自然支援以時間作為條件排序,完美滿足我們選出要推送的記錄。 

ok~ 既然方案已經有了那如何在一天時間內把這個方案落地呢?是的我設計出這個方案到基本編碼完成,時間就是一天。 。 。因為時間太趕鳥。 

首先我們以 user_id 為 key,然後 mod 佇列數​​ hash 到 redis SortedSet 佇列裡面。為什麼要這樣呢,因為如果用戶同時訂閱了兩張劵並且推送時間很近,這樣的兩條推送就可以合併成一條~,並且這樣 hash 也相對均勻。以下是部分程式碼的截圖:   

淺談redis實現即時訂閱推播的三種方法

然後要決定佇列的數量,一般正常來說我們有多少台處理的伺服器就定義多少個佇列。因為隊列太少,會造成隊列競爭,太多可能會導致記錄無法及時處理。然而最佳實踐是佇列數量應該是可動態配置化的,因為線上的群集機器數是會經常變的。

大促的時候我們會加機器是不是,而且業務量成長了,機器數也是會增加是不是~。所以我是藉用了淘寶的 diamond 進行佇列數的動態配置。

淺談redis實現即時訂閱推播的三種方法

我們每次從佇列裡面取多少筆記錄也是可以動態設定的 

淺談redis實現即時訂閱推播的三種方法

這樣就可以隨時根據實際的生產情況調整整個集群的吞吐量~。  所以我們的定時任務群集還是有一個特性就是支援動態調整~。最後一個關鍵元件就是負載平衡了。這個是非常重要的!

因為這個做得不好就會可能導致多台機競爭同時處理一個佇列,影響整個叢集的效率!在時間很緊迫的情況下我就用了一個簡單實用的利用 redis 一個自增 key 然後 mod 隊列數量算法。這樣就很大程度上就保證不會有兩台機器同時去競爭一條隊列~.

淺談redis實現即時訂閱推播的三種方法

#更多程式相關知識,請造訪:程式影片! !

以上是淺談redis實現即時訂閱推播的三種方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除