摘要:?背景 6月14日晚,2018年俄羅斯世界杯在莫斯科開幕。國(guó)內(nèi)數(shù)以千萬(wàn)的觀眾通過(guò)優(yōu)酷、央視影音或者是咪咕視頻觀看了此次開幕賽。阿里云公布的一份數(shù)據(jù)顯示,第一波流量洪峰出現(xiàn)在揭幕戰(zhàn)開場(chǎng)后的第44分鐘,峰值達(dá)到了1.5個(gè)2018年春晚的規(guī)模。
背景
6月14日晚,2018年俄羅斯世界杯在莫斯科開幕。國(guó)內(nèi)數(shù)以千萬(wàn)的觀眾通過(guò)優(yōu)酷、央視影音或者是咪咕視頻觀看了此次開幕賽。
阿里云公布的一份數(shù)據(jù)顯示,第一波流量洪峰出現(xiàn)在揭幕戰(zhàn)開場(chǎng)后的第44分鐘,峰值達(dá)到了1.5個(gè)2018年春晚的規(guī)模。自此,本屆世界杯也成為了史上最大規(guī)模的一次在線直播。比賽期間,預(yù)計(jì)全網(wǎng)70%的世界杯直播流量都跑在了阿里云上。(注:上述內(nèi)容引用自:https://www.leiphone.com/news/201806/Du10JxOxuJ6Ou782.html,所屬權(quán)歸原作者所有。)
細(xì)心的網(wǎng)友們肯定已經(jīng)注意到了,今年的世界杯與以往的世界杯相比,不僅比賽結(jié)果出人意料,而且觀看比賽的APP客戶端中也增加了豐富的互動(dòng)和紅包驚喜,眾平臺(tái)為了引流和激活“僵尸”用戶也是使出了渾身解數(shù)。下面一起來(lái)盤點(diǎn)一下精彩世界杯背后你看得見的Redis身影吧!主要從業(yè)務(wù)架構(gòu)、應(yīng)用場(chǎng)景、高可用建設(shè)以及彈性擴(kuò)縮容等幾個(gè)方面進(jìn)行展開。
業(yè)務(wù)架構(gòu)
????????????????????????????(圖來(lái)源自網(wǎng)絡(luò))
上圖是某企業(yè)直播解決方案,主要使用了彈性計(jì)算、CDN、智能動(dòng)態(tài)編碼技術(shù)、視頻AI、窄帶高清2.0、Redis等等產(chǎn)品技術(shù),充分保證了超清畫質(zhì)體驗(yàn)的同時(shí),節(jié)省了帶寬的消耗,并最大限度優(yōu)化資源和成本,給觀眾帶來(lái)流暢和豐富多彩的互動(dòng)體驗(yàn),極大的滿足了用戶的參與感。當(dāng)然,本文重點(diǎn)不是談?wù)撘曨l如何從錄制到播出的,而是要探討一下“數(shù)據(jù)庫(kù)服務(wù)器”中Redis在豐富觀看直播體驗(yàn)中發(fā)揮的重要作用和使用場(chǎng)景及實(shí)現(xiàn)。
應(yīng)用場(chǎng)景
Redis之所以能夠被廣泛的應(yīng)用于企業(yè)的架構(gòu)中,而且是不可或缺的重要組成部分,也可以說(shuō)是標(biāo)配,其中很重要的一點(diǎn)就是得益于它具有豐富的數(shù)據(jù)結(jié)構(gòu),這也是它逐漸替代Memcached,備受青睞的重要原因。它的數(shù)據(jù)結(jié)構(gòu)有:String、List、Hash、set、Sorted set、bitmap、bit field、hyperLogLog、Geospatial Index等。
正是因?yàn)橛辛诉@些數(shù)據(jù)結(jié)構(gòu)和Redis技術(shù)的不斷完善和發(fā)展,才被廣泛應(yīng)用于各行各業(yè)中,應(yīng)用場(chǎng)景也是百花齊放。比如:會(huì)話緩存(Session cache)、全頁(yè)緩存(FPC)、手機(jī)驗(yàn)證碼、訪問(wèn)頻率限制/黑白名單、消息隊(duì)列、發(fā)布與訂閱、消息通知、排名/排行榜/最新列表、計(jì)數(shù)器(比如微博的轉(zhuǎn)評(píng)贊計(jì)數(shù)、閱讀數(shù)(瀏覽數(shù),視頻播放計(jì)數(shù))、博文數(shù)(發(fā)帖數(shù))、粉絲數(shù)、關(guān)注數(shù)(喜歡商品數(shù))、未讀數(shù)(動(dòng)態(tài)數(shù)))、共同好友/喜好/標(biāo)簽、推送、下拉刷新、私信、商品庫(kù)存管理(限時(shí)的優(yōu)惠活動(dòng)信息)、證券指標(biāo)實(shí)時(shí)計(jì)算,發(fā)號(hào)器/UUID、以及隨著LBS(基于位置服務(wù))的發(fā)展,加入的GEO(地理信息定位)的功能和基于Lua自定義命令或功能等等。大家在使用過(guò)程中,需要結(jié)合自己的業(yè)務(wù)場(chǎng)景,選擇正確的數(shù)據(jù)類型。那么下面以“優(yōu)酷APP”為例,來(lái)看看有哪些看得見的場(chǎng)景用到了Redis技術(shù)。
聊天列表(評(píng)論列表)
這種評(píng)論列表可以用Hash來(lái)實(shí)現(xiàn),比如:{userID:contents}
用戶ID評(píng)論內(nèi)容11101000破產(chǎn)了關(guān)于評(píng)論,你有沒有想過(guò)一個(gè)問(wèn)題,“垃圾評(píng)論” 怎么在評(píng)論列表里看不到?沒有用戶一直在刷評(píng)論的?是的,這個(gè)肯定是spam系統(tǒng)在默默工作的,這個(gè)Redis也是可以做到的,更多策略/機(jī)制需要結(jié)合drools配合完成。當(dāng)然通常都是有專門的spam的系統(tǒng)來(lái)實(shí)現(xiàn)。舉個(gè)例子,比如要限制某個(gè)用戶一分鐘內(nèi)的評(píng)論次數(shù):
Method1:?用sorted set實(shí)現(xiàn)。將最近一天用戶評(píng)論操作記錄起來(lái), score用timestamp替代得分,然后通過(guò)Zset的命令RANGEBYSCORE、ZADD、ZRANGEBYSCORE結(jié)合實(shí)現(xiàn)
RANGEBYSCORE userID:10000:operation:comment 61307510405600 +inf //獲得1分鐘內(nèi)的操作記錄
redis> ZADD userID:10000:operation:comment 61307510402300 "這是一條評(píng)論" //score 為timestamp (integer) 1
redis> ZRANGEBYSCORE userID:10000:operation:comment 61307510405600 +inf //獲得1分鐘內(nèi)的操作記錄
Method2:?用Redis+Lua實(shí)現(xiàn)訪問(wèn)頻率控制
#?KEYS[1]表示限制次數(shù),由調(diào)用程序傳入local?key1?,key2?=?'access:limit'?,'access:expire'if?redis.call('EXISTS'?,key2)?>?0?then ???return?redis.call('DECR'?,key1)?;else? ???redis.call('SET'?,key2?,1) ???redis.call('EXPIRE'?,key2?,60) ???redis.call('SET'?,key1?,KEYS[1])???return?KEYS[1] end
賽況(賽事列表)
最新活動(dòng)列表,可以用Redis的List數(shù)據(jù)結(jié)構(gòu)或者sorted set數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),加上過(guò)期時(shí)間輕松搞定。同樣的方法,也能輕松的實(shí)現(xiàn)最新商品列表、各種排行榜等。
消息(未讀消息數(shù))
實(shí)現(xiàn)思路是:使用hash存儲(chǔ)用戶上次看過(guò)的時(shí)間,使用sorted set存儲(chǔ)每個(gè)模塊(評(píng)論區(qū)、群聊)的每個(gè)信息產(chǎn)生的時(shí)間,并記錄未讀消息的數(shù)量,實(shí)現(xiàn)可以參考:https://yq.aliyun.com/ziliao/92283
點(diǎn)贊(點(diǎn)贊數(shù))
點(diǎn)贊數(shù),實(shí)現(xiàn)相對(duì)比較簡(jiǎn)單了,用string數(shù)據(jù)結(jié)構(gòu)或者Hash數(shù)據(jù)結(jié)構(gòu)都能實(shí)現(xiàn),這種點(diǎn)贊沒有取消的操作,直接Incr即可。假設(shè)key為場(chǎng)次編號(hào):active16
string:
INCR active16
GET active16
Hash:
HSET active:active16 zan 0
HINCRBY active:active16 zan 1
HGETALL active:active16
紅包雨(傳送門、紅包雨)、商品庫(kù)存/紅包金額管理
下面介紹一種基于Redis的搶紅包方案。
把原始的紅包稱為大紅包,拆分后的紅包稱為小紅包。
1、小紅包預(yù)先生成,插到數(shù)據(jù)庫(kù)里,紅包對(duì)應(yīng)的用戶ID是null。
2、每個(gè)大紅包對(duì)應(yīng)兩個(gè)Redis隊(duì)列,一個(gè)是未消費(fèi)紅包隊(duì)列,另一個(gè)是已消費(fèi)紅包隊(duì)列。開始時(shí),把未搶的小紅包全放到未消費(fèi)紅包隊(duì)列里。
未消費(fèi)紅包隊(duì)列里是json字符串,activeID是活動(dòng)場(chǎng)次,money是紅包金額,product是商品個(gè)數(shù)。如{activeId:'16', money:'300'} 或 {activeID:'16',product:'50'}
3、在Redis中用一個(gè)map來(lái)過(guò)濾已搶到紅包的用戶。
4、搶紅包時(shí),先判斷用戶是否搶過(guò)紅包,如果沒有,則從未消費(fèi)紅包隊(duì)列中取出一個(gè)小紅包,再push到另一個(gè)已消費(fèi)隊(duì)列中,最后把用戶ID放入去重的map中。
5、用一個(gè)單線程批量把已消費(fèi)隊(duì)列里的紅包取出來(lái),再批量update紅包的用戶ID到數(shù)據(jù)庫(kù)里。
上面的流程是很清楚的,但是在第4步時(shí),如果是用戶快速點(diǎn)了兩次,或者開了兩個(gè)瀏覽器來(lái)?yè)尲t包,會(huì)不會(huì)有可能用戶搶到了兩個(gè)紅包?
為了解決這個(gè)問(wèn)題,采用了lua腳本方式,讓第4步整個(gè)過(guò)程是原子性地執(zhí)行。
下面是在Redis上執(zhí)行的Lua腳本:
-- 函數(shù):嘗試獲得紅包,如果成功,則返回json字符串,如果不成功,則返回空
-- 參數(shù):紅包隊(duì)列名, 已消費(fèi)的隊(duì)列名,去重的Map名,用戶ID
-- 返回值:nil 或者 json字符串,包含用戶ID:userId,紅包ID:id,紅包金額:money
-- 如果用戶已搶過(guò)紅包,則返回nil
if redis.call('hexists', KEYS[3], KEYS[4]) ~= 0 then
return nil
else
-- 先取出一個(gè)小紅包
local hongBao = redis.call('rpop', KEYS[1]);
if hongBao then
local?x?=?cjson.decode(hongBao); --?加入用戶ID信息 x['userId']?=?KEYS[4];local?re?=?cjson.encode(x); --?把用戶ID放到去重的set里 redis.call('hset',?KEYS[3],?KEYS[4],?KEYS[4]); --?把紅包放到已消費(fèi)隊(duì)列里 redis.call('lpush',?KEYS[2],?re);return?re;
end
end
return nil
參考:https://blog.csdn.net/hengyunabc/article/details/19433779
消息推送
上圖中的這種彈出消息,未必是Redis實(shí)現(xiàn)或者說(shuō)肯定不是,哈哈。但是這里想提示的是消息提醒,Redis是可以實(shí)現(xiàn)的。
這種用pub/sub機(jī)制來(lái)實(shí)現(xiàn)的消息通知,沒有持久化機(jī)制,屬于即發(fā)即棄模式。生產(chǎn)者不需要關(guān)心有多少的訂閱者,也不用關(guān)心訂閱者的具體信息,在線的客戶端(消費(fèi)者)正常情況下是都看到的,正如我們關(guān)注的某個(gè)節(jié)目一樣,在線的時(shí)候總能關(guān)注的節(jié)目更新通知一樣。
高可用建設(shè)和彈性擴(kuò)展
面對(duì)世界杯這種全球性的全民賽事,尤其是在大家都比較關(guān)注的明星或球隊(duì)對(duì)抗的時(shí)候(朋友圈都被刷屏的那種),那壓力可想而知,雖然拿不到具體的數(shù)據(jù),但是從我在微博時(shí)保障的熱點(diǎn)事件來(lái)看,也能猜著個(gè)大概。它跟微博熱點(diǎn)有很多的相似點(diǎn),具有不可預(yù)見性和突發(fā)性,并且伴隨著極短時(shí)間內(nèi)流量的數(shù)倍增長(zhǎng),甚至更多,有時(shí)持續(xù)時(shí)間較長(zhǎng)。如何快速應(yīng)對(duì)突發(fā)流量的沖擊,確保線上服務(wù)的穩(wěn)定性,是一個(gè)非常巨大的挑戰(zhàn)和有意義的事情。為了達(dá)到這一目標(biāo),首先需要有一個(gè)完善的,穩(wěn)定可靠的,健壯的數(shù)據(jù)庫(kù)運(yùn)維體系來(lái)提供支撐和管理。
正如前面一開始提到的,優(yōu)酷、央視影音、咪咕視頻等視頻平臺(tái),為了保障業(yè)務(wù)穩(wěn)定、減少成本(不可能為了短短一個(gè)月的賽事準(zhǔn)備4年才用到一次的基礎(chǔ)設(shè)施),選擇和公有云結(jié)合是最佳選擇,這也是阿里云之所以有流量洪峰出現(xiàn)的重要原因。那么對(duì)于Redis來(lái)說(shuō)如何去建設(shè)高可用服務(wù)以及解決彈性擴(kuò)展問(wèn)題?主要有兩點(diǎn):
高可用:異地災(zāi)備和多活能力
“不要把雞蛋放到同一個(gè)籃子里”,相信很多架構(gòu)師也肯定會(huì)這么去想,也肯定是這么做的。可以自己搭建一套高可用架構(gòu),也可以直接采用阿里云Redis服務(wù)提供的異地災(zāi)備和多活能力和, 實(shí)例部署在跨region,自動(dòng)雙向同步。(參考資料:https://help.aliyun.com/document_detail/71881.html?spm=a2c4g.11186623.6.660.4NtWyS)
通過(guò)異地災(zāi)備和多活的能力,一旦發(fā)生故障,還可以通過(guò)異地快速接管業(yè)務(wù),確保使用體驗(yàn)。另外,很多分布較廣的業(yè)務(wù),用戶需要跨地域遠(yuǎn)距離訪問(wèn)服務(wù)。如果此時(shí)訪問(wèn)延遲大,將直接影響用戶體驗(yàn)。云數(shù)據(jù)庫(kù)Redis提供的云上多活,還可以幫助用戶消除跨地域遠(yuǎn)距離訪問(wèn)時(shí)的延遲大問(wèn)題。
彈性:資源伸縮、讀寫分離
一個(gè)區(qū)域出現(xiàn)訪問(wèn)異常后,仍然能通過(guò)一些手段,比如降級(jí)、切流量、限流等一系列措施來(lái)保障服務(wù)的穩(wěn)定性。在云模式下,當(dāng)業(yè)務(wù)量上來(lái)了,扛不住的時(shí)候,可以通過(guò)阿里云Redis服務(wù)自動(dòng)具備彈性擴(kuò)縮容一勞永逸,還可以精打細(xì)算使用讀寫分離功能小成本卸載讀壓力或者通過(guò)混合存儲(chǔ)卸載存儲(chǔ)成本等等。(參考資料:https://help.aliyun.com/document_detail/65001.html?spm=a2c4g.11186631.6.612.JcCjqf)
總之,Redis是一個(gè)非常重要的組件,它能夠被廣泛的應(yīng)用于企業(yè)的架構(gòu)中,而且是不可或缺的重要組成部分。
如果之前沒有了解過(guò),那么,“Redis,請(qǐng)了解一下”!不足之處,歡迎批評(píng)指正。
作者簡(jiǎn)介:
張冬洪:阿里云MVP,極數(shù)云舟對(duì)外合作部總監(jiān)、技術(shù)專家,Redis中國(guó)用戶組主席,中國(guó)MySQL用戶組主席團(tuán)成員
點(diǎn)擊以下鏈接報(bào)名
https://yq.aliyun.com/event/288/join/pre
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載
評(píng)論