我是如何用 redis 做實時訂閱推送的

2021-10-22 17:45:34 字數 3237 閱讀 9012

小hub領讀:

20w+的推送使用者,如何做到秒級併發完成,文中分別介紹了mq、傳統定時任務以及redis的sortset佇列三種方案,一一分析可行性,並且最後給出了redis的邏輯與部分**實現。你學會了嗎?

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

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

什麼是領劵的訂閱推送?

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

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

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

推送的體量大:爆款的神劵,人人都想搶!

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

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

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

方案 1:

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

方案 2:

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

能否支援多機同時跑

一般不能,同一時刻只能單機跑。

儲存資料來源

一般是 mysql 或者其它傳統資料庫,並且是單錶儲存

頻率支援秒、分、時、天,一般不能太快

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

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

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

單點故障。萬一跑的那台機掛了,那整個業務不可用了 -。- 這是乙個很可怕的事情!

所以傳統定時任務也不太適合這個業務。。。 

那我們是不是就束手無策了呢?其實不是的! 我們只要對傳統的定時任務做乙個簡單的改造!就可以把它變成可以同時多機跑, 並且實效性可以精確到秒級,並且拒絕單點故障的定時任務集群!這其中就要借助我們的強大的 redis 了。

方案 3:定時任務集群

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

1、實效性要高

2、吞吐量要大

3、服務要穩定,不能有單點故障 

下面是整個定時任務集群的架構圖。 

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

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

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

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

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

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

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

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

然後要決定佇列的數量,一般正常來說我們有多少臺處理的伺服器就定義多少條佇列。因為佇列太少,會造成佇列競爭,太多可能會導致記錄得不到及時處理。

然而最佳實踐是佇列數量應該是可動態配置化的,因為線上的集群機器數是會經常變的。大促的時候我們會加機器是不是,並且業務量增長了,機器數也是會增加是不是~。所以我是借用了**的 diamond 進行佇列數的動態配置。

我們每次從佇列裡面取多少條記錄也是可以動態配置的 

這樣就可以隨時根據實際的生產情況調整整個集群的吞吐量~。  所以我們的定時任務集群還是具有乙個特性就是支援動態調整~。

最後乙個關鍵元件就是負載均衡了。這個是非常重要的!因為這個做得不好就會可能導致多台機競爭同時處理乙個佇列,影響整個集群的效率!在時間很緊的情況下我就用了乙個簡單實用的利用 redis 乙個自增 key 然後 mod 佇列數量演算法。這樣就很大程度上就保證不會有兩台機器同時去競爭一條佇列~.

最後我們算一下整個集群的吞吐量

10(機器數) * 2000(一次拉取數) = 20000。然後以 mq 的形式把訊息推送到訊息中心,發 mq 是非同步的,算上其它處理 0.5s。

其實傳送 20w 的推送也就是 10 幾 s 的事情。

ok~ 到這裡我們整個定時任務集群就差不多基本落地好了。如果你問我後面還有什麼可以完善的話那就是:

目前專案已上前線,執行平穩~。

如何用Redis做LRU Cache

lru least recently used 最近最少使用演算法是眾多置換演算法中的一種。redis中有乙個maxmemory概念,主要是為了將使用的記憶體限定在乙個固定的大小。redis用到的lru 演算法,是一種近似的lru演算法。上面已經說過maxmemory是為了限定redis最大記憶體使...

我是如何用Worktile進行敏捷開發的

產品backlog是scrum的核心,也是一切的起源。從根本上說,它就是乙個需求 或故事 或特性等組成的列表,按照重要性的級別進行了排序。它裡面包含的是客戶想要的東西,並用客戶的術語加以描述。一般來說產品backlog需要包含以下幾個重要的屬性 這時候worktile的優勢就體現出來了 workti...

我是如何做知識付費的

對這個領域可能你了解過,也可能沒有了解過,沒關係,通過本課程的講解,或許對你有一些幫助。可能也有人會問,知識付費真的可以做嗎,我又適不適合去做呢?我可以告訴你,任何乙個行業,都可以通過知識付費來做一遍,而且,許多人已經依靠做知識付費每年收入過百萬,所以,不要質疑能不能做,行動起來才是真正要做的。課程...