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