<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 3121字,需瀏覽 7分鐘

           ·

          2021-03-31 12:33

          超全面!Java核心知識總結(jié)(點(diǎn)擊查看)


          超全面!Java核心知識總結(jié)(點(diǎn)擊查看)


          前陣子開發(fā)了公司領(lǐng)劵中心的項目,這個項目是以 redis 作為關(guān)鍵技術(shù)落地的。

          先說一下領(lǐng)劵中心的項目吧,這個項目就類似京東 app 的領(lǐng)劵中心,當(dāng)然圖是截取京東的,公司的就不截了。。。

               

          其中有一個功能叫做領(lǐng)劵的訂閱推送。

          什么是領(lǐng)劵的訂閱推送?

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

          本來這個訂閱功能應(yīng)該是消息中心那邊做的,但他們說這個短時間內(nèi)做不了。所以讓我這個負(fù)責(zé)優(yōu)惠劵的做了 -.-!。具體方案就是到具體的推送時間點(diǎn)了,coupon 系統(tǒng)調(diào)用消息中心的推送接口,把信息推送出去。

          下們我們分析一下這個功能的業(yè)務(wù)情景。公司目前注冊用戶 6000W+,是哪家就不要打聽了。。。比如有一張無門檻的優(yōu)惠劵下單立減 20 元,那么搶這張劵的人就會比較多,我們保守估計 10W+,百萬級別不好說。我們初定為 20W 萬人,那么這 20W 條推送信息要在一分鐘推送完成!并且一個用戶是可以訂閱多張劵的。所以我們知道了這個訂閱功能的有兩個突出的難點(diǎn):

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

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

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

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

          推送的實效性的問題:當(dāng)用戶在領(lǐng)劵中心訂閱了某個劵的領(lǐng)取提醒后,在后臺就會生成一條用戶的訂閱提醒記錄,里面記錄了在哪個時間點(diǎn)給用戶發(fā)送推送信息。所以問題就變成了系統(tǒng)如何快速實時選出哪些要推送的記錄!

          方案 1:

          MQ 的延遲投遞。MQ 雖然支持消息的延遲投遞但尺度太大 1s 5s 10s 30s 1m,用來做精確時間點(diǎn)投遞不行!并且用戶執(zhí)行訂閱之后又取消訂閱的話,要把發(fā)出去的 MQ 消息 delete 掉這個操作有點(diǎn)頭大,短時間內(nèi)難以落地!并且用戶可以取消之后再訂閱,這又涉及到去重的問題。所以 MQ 的方案否掉。

          方案 2:

          傳統(tǒng)定時任務(wù)。這個相對來說就簡單一點(diǎn),用定時任務(wù)是去 db 里面 load 用戶的訂閱提醒記錄,從中選出當(dāng)前可以推送的記錄。但有句話說得好任何脫離實際業(yè)務(wù)的設(shè)計都是耍流氓~。下面我們就分析一下傳統(tǒng)的定時任務(wù)到底適不適合我們的這個業(yè)務(wù)!

          能否支持多機(jī)同時跑一般不能,同一時刻只能單機(jī)跑。
          存儲數(shù)據(jù)源一般是 mysql 或者其它傳統(tǒng)數(shù)據(jù)庫,并且是單表存儲
          頻率支持秒、分、時、天,一般不能太快

          綜上所述我們就知道了一般傳統(tǒng)的定時任務(wù)存在以下缺點(diǎn):

          1. 性能瓶頸。只有一臺機(jī)在處理,在大體量數(shù)據(jù)面前力不從心!

          2. 實效性差。定時任務(wù)的頻率不能太高,太高會業(yè)務(wù)數(shù)據(jù)庫造成很大的壓力!

          3. 單點(diǎn)故障。萬一跑的那臺機(jī)掛了,那整個業(yè)務(wù)不可用了 -。- 這是一個很可怕的事情!

          所以傳統(tǒng)定時任務(wù)也不太適合這個業(yè)務(wù)。。。 

          那我們是不是就束手無策了呢?其實不是的! 我們只要對傳統(tǒng)的定時任務(wù)做一個簡單的改造!就可以把它變成可以同時多機(jī)跑, 并且實效性可以精確到秒級,并且拒絕單點(diǎn)故障的定時任務(wù)集群!這其中就要借助我們的強(qiáng)大的 redis 了。

          方案 3:定時任務(wù)集群

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

               1、實效性要高

               2、吞吐量要大

               3、服務(wù)要穩(wěn)定,不能有單點(diǎn)故障 

          下面是整個定時任務(wù)集群的架構(gòu)圖。 

          架構(gòu)很簡單:我們把用戶的訂閱推送記錄存儲到 redis 集群的 sortedSet 隊列里面, 并且以提醒用戶提醒時間戳作為 score 值,然后在我們個每業(yè)務(wù) server 里面起一個定時器頻率是秒級,我的設(shè)定就是 1s,然后經(jīng)過負(fù)載均衡之后從某個隊列里面獲取要推送的用戶記錄進(jìn)行推送。下面我們分析以下這個架構(gòu)。

              1、性能:除去帶寬等其它因素,基本與機(jī)器數(shù)成線性相關(guān)。機(jī)器數(shù)量越多吞吐量越大,機(jī)器數(shù)量少時相對的吞吐量就減少。

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

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

          這里解析一下為什么用 redis?

          第一 redis 可以作為一個高性能的存儲 db,性能要比 MySQL 好很多,并且支持持久化,穩(wěn)定性好。

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

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

          首先我們以 user_id 作為 key,然后 mod 隊列數(shù) hash 到 redis SortedSet 隊列里面。為什么要這樣呢,因為如果用戶同時訂閱了兩張劵并且推送時間很近,這樣的兩條推送就可以合并成一條~,并且這樣 hash 也相對均勻。下面是部分代碼的截圖:

            

          然后要決定隊列的數(shù)量,一般正常來說我們有多少臺處理的服務(wù)器就定義多少條隊列。因為隊列太少,會造成隊列競爭,太多可能會導(dǎo)致記錄得不到及時處理。

          然而最佳實踐是隊列數(shù)量應(yīng)該是可動態(tài)配置化的,因為線上的集群機(jī)器數(shù)是會經(jīng)常變的。大促的時候我們會加機(jī)器是不是,并且業(yè)務(wù)量增長了,機(jī)器數(shù)也是會增加是不是~。所以我是借用了淘寶的 diamond 進(jìn)行隊列數(shù)的動態(tài)配置。

           

          我們每次從隊列里面取多少條記錄也是可以動態(tài)配置的 

          這樣就可以隨時根據(jù)實際的生產(chǎn)情況調(diào)整整個集群的吞吐量~。  所以我們的定時任務(wù)集群還是具有一個特性就是支持動態(tài)調(diào)整~。

          最后一個關(guān)鍵組件就是負(fù)載均衡了。這個是非常重要的!因為這個做得不好就會可能導(dǎo)致多臺機(jī)競爭同時處理一個隊列,影響整個集群的效率!在時間很緊的情況下我就用了一個簡單實用的利用 redis 一個自增 key 然后 mod 隊列數(shù)量算法。這樣就很大程度上就保證不會有兩臺機(jī)器同時去競爭一條隊列~.

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

          10(機(jī)器數(shù)) * 2000(一次拉取數(shù)) = 20000。然后以 MQ 的形式把消息推送到消息中心,發(fā) MQ 是異步的,算上其它處理 0.5s。

          其實發(fā)送 20W 的推送也就是 10 幾 s 的事情。

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

          • 加監(jiān)控, 集群怎么可以木有監(jiān)控呢,萬一出問題有任務(wù)堆積怎么辦~

          • 加上可視化界面。

          • 最好有智能調(diào)度,增加任務(wù)優(yōu)先級。優(yōu)先級高的任務(wù)先運(yùn)行嘛。

          • 資源調(diào)度,萬一機(jī)器數(shù)量不夠,力不從心,優(yōu)先保證重要任務(wù)執(zhí)行。

          目前項目已上前線,運(yùn)行平穩(wěn)~。


          如有文章對你有幫助,

          在看”和轉(zhuǎn)發(fā)是對我最大的支持!


          推薦 GitHub 書籍倉庫
          https://github.com/ebooklist/awesome-ebooks-list

          整理了大部分常用 技術(shù)書籍PDF,持續(xù)更新中... 你需要的技術(shù)書籍,這里可能都有...


          點(diǎn)擊文末“閱讀原文”可直達(dá)

          整理不易,麻煩各位小伙伴在GitHub中來個一鍵三連

          瀏覽 48
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲精品国产免费 | 影音先锋男人的资源 | 精品人妻一区二区三区奶水 | 欧差XXX性受XYX性爽 | 国产像蕉9 |