自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

我是如何用Redis做實(shí)時(shí)訂閱推送的

開(kāi)發(fā) 架構(gòu) Redis
文中分別介紹了MQ、傳統(tǒng)定時(shí)任務(wù)以及Redis的SortSet隊(duì)列三種方案,一一分析可行性,并且最后給出了Redis的邏輯與部分代碼實(shí)現(xiàn)。

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

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

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

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

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

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

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

  1.  推送的實(shí)效性:推送慢了,用戶(hù)會(huì)抱怨沒(méi)有及時(shí)通知他們錯(cuò)過(guò)了開(kāi)搶時(shí)機(jī)。
  2.  推送的體量大:爆款的神劵,人人都想搶?zhuān)?/li>

然而推送體量又會(huì)影響到推送的實(shí)效性。這真是一個(gè)讓人頭疼的問(wèn)題!

那就讓我們把問(wèn)題一個(gè)個(gè)解決掉吧!

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

方案 1:

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

方案 2:

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

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

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

  1.  性能瓶頸。只有一臺(tái)機(jī)在處理,在大體量數(shù)據(jù)面前力不從心!
  2.  實(shí)效性差。定時(shí)任務(wù)的頻率不能太高,太高會(huì)業(yè)務(wù)數(shù)據(jù)庫(kù)造成很大的壓力!
  3.  單點(diǎn)故障。萬(wàn)一跑的那臺(tái)機(jī)掛了,那整個(gè)業(yè)務(wù)不可用了 -。- 這是一個(gè)很可怕的事情!

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

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

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

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

     1、實(shí)效性要高

     2、吞吐量要大

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

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

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

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

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

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

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

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

第二 redis SortedSet 隊(duì)列天然支持以時(shí)間作為條件排序,完美滿(mǎn)足我們選出要推送的記錄。

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

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

然后要決定隊(duì)列的數(shù)量,一般正常來(lái)說(shuō)我們有多少臺(tái)處理的服務(wù)器就定義多少條隊(duì)列。因?yàn)殛?duì)列太少,會(huì)造成隊(duì)列競(jìng)爭(zhēng),太多可能會(huì)導(dǎo)致記錄得不到及時(shí)處理。

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

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

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

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

最后我們算一下整個(gè)集群的吞吐量

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

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

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

  •  加監(jiān)控, 集群怎么可以木有監(jiān)控呢,萬(wàn)一出問(wèn)題有任務(wù)堆積怎么辦~
  •  加上可視化界面。
  •  最好有智能調(diào)度,增加任務(wù)優(yōu)先級(jí)。優(yōu)先級(jí)高的任務(wù)先運(yùn)行嘛。
  •  資源調(diào)度,萬(wàn)一機(jī)器數(shù)量不夠,力不從心,優(yōu)先保證重要任務(wù)執(zhí)行。

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

 

責(zé)任編輯:龐桂玉 來(lái)源: JAVA高級(jí)架構(gòu)
相關(guān)推薦

2017-05-02 13:38:51

CSS繪制形狀

2015-04-14 09:31:10

AWSAWS PaaSSaaS可視化編排

2020-06-28 08:34:07

架構(gòu)師阿里軟件

2018-08-10 14:57:03

UnixMySQL命令

2012-05-24 14:58:55

開(kāi)源代碼

2021-07-06 10:03:05

軟件開(kāi)發(fā) 技術(shù)

2022-07-30 10:08:06

MQTT?協(xié)議物聯(lián)網(wǎng)

2014-12-23 14:14:54

2017-05-02 20:56:36

機(jī)器學(xué)習(xí)HR簡(jiǎn)歷

2017-04-11 17:22:57

編程程序員語(yǔ)言

2012-11-28 01:47:35

軟件測(cè)試測(cè)試

2021-06-09 10:15:26

優(yōu)化性能顆粒度

2019-09-15 14:07:49

2016-09-17 00:12:46

2021-06-22 15:06:13

Redis客戶(hù)端 Redis-clie

2022-02-20 19:02:16

RollupVue 2JavaScrip

2013-01-10 12:57:23

產(chǎn)品經(jīng)理App產(chǎn)品設(shè)計(jì)

2020-04-21 08:30:32

AI人工智能語(yǔ)言

2023-05-10 09:04:10

promise場(chǎng)景業(yè)務(wù)

2017-05-09 09:26:48

微服務(wù)消息推送
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)