社交軟件紅包技術(shù)解密(十二):解密抖音春節(jié)紅包背后的技術(shù)設(shè)計(jì)與實(shí)踐

本文由字節(jié)跳動(dòng)技術(shù)團(tuán)隊(duì)開發(fā)工程師王浩分享,有較多修訂。
1、引言
對(duì)于移動(dòng)互聯(lián)網(wǎng)時(shí)代的用戶來(lái)說(shuō),短視頻應(yīng)用再也不是看看視頻就完事,尤其抖音這種頭部應(yīng)用,已經(jīng)是除了傳統(tǒng)IM即時(shí)通訊軟件以外的新型社交產(chǎn)品了。
對(duì)于中國(guó)人一年一度最重的節(jié)日——春節(jié)來(lái)說(shuō),紅包是必不可少的節(jié)日特定社交元素,而抖音自然不會(huì)被錯(cuò)過(guò)。在2022年的春節(jié)活動(dòng)期間,抖音將視頻和春節(jié)紅包相結(jié)合,用戶可以通過(guò)拍視頻發(fā)紅包的方式來(lái)給粉絲和好友送祝福。
本文將要分享的是春節(jié)期間海量紅包社交活動(dòng)為抖音所帶來(lái)的各種技術(shù)挑戰(zhàn),以及抖音技術(shù)團(tuán)隊(duì)是如何在實(shí)踐中一一解決這些問題的。?

學(xué)習(xí)交流:
- 移動(dòng)端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動(dòng)端IM》
- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點(diǎn)此)
(本文已同步發(fā)布于:http://www.52im.net/thread-3945-1-1.html)
2、系列文章
《社交軟件紅包技術(shù)解密(一):全面解密QQ紅包技術(shù)方案——架構(gòu)、技術(shù)實(shí)現(xiàn)等》
《社交軟件紅包技術(shù)解密(二):解密微信搖一搖紅包從0到1的技術(shù)演進(jìn)》
《社交軟件紅包技術(shù)解密(三):微信搖一搖紅包雨背后的技術(shù)細(xì)節(jié)》
《社交軟件紅包技術(shù)解密(四):微信紅包系統(tǒng)是如何應(yīng)對(duì)高并發(fā)的》
《社交軟件紅包技術(shù)解密(五):微信紅包系統(tǒng)是如何實(shí)現(xiàn)高可用性的》
《社交軟件紅包技術(shù)解密(六):微信紅包系統(tǒng)的存儲(chǔ)層架構(gòu)演進(jìn)實(shí)踐》
《社交軟件紅包技術(shù)解密(七):支付寶紅包的海量高并發(fā)技術(shù)實(shí)踐》
《社交軟件紅包技術(shù)解密(八):全面解密微博紅包技術(shù)方案》
《社交軟件紅包技術(shù)解密(九):談?wù)勈諵春節(jié)紅包的設(shè)計(jì)、容災(zāi)、運(yùn)維、架構(gòu)等》
《社交軟件紅包技術(shù)解密(十):手Q客戶端針對(duì)2020年春節(jié)紅包的技術(shù)實(shí)踐》
《社交軟件紅包技術(shù)解密(十一):最全解密微信紅包隨機(jī)算法(含演示代碼)》
《社交軟件紅包技術(shù)解密(十二):解密抖音春節(jié)紅包背后的技術(shù)設(shè)計(jì)與實(shí)踐》(* 本文)
3、先看看紅包業(yè)務(wù)玩法
抖音的整個(gè)紅包活動(dòng)玩法分為 B2C 和 C2C 兩種玩法,下面對(duì)這兩個(gè)玩法的流程簡(jiǎn)單介紹下,也方便讀者理解后續(xù)的技術(shù)問題和解決思路。
3.1 B2C 紅包
在 B2C 紅包玩法中,用戶需要先來(lái)抖音或者抖 Lite 參加春節(jié)紅包雨活動(dòng),有一定概率在春節(jié)紅包雨活動(dòng)中獲得紅包補(bǔ)貼。
用戶可以在獲得補(bǔ)貼后直接跳轉(zhuǎn)到相機(jī)頁(yè)面,或者在之后拍攝視頻跳轉(zhuǎn)到相機(jī)頁(yè)面,在相機(jī)頁(yè)面用戶拍攝完視頻后會(huì)看到一個(gè)紅包掛件,在掛件中可以看到已發(fā)放補(bǔ)貼。
用戶選擇補(bǔ)貼后點(diǎn)擊下一步完成投稿后即可完成視頻紅包的發(fā)放。

?
如上圖所示,從左至左:“圖1”是春節(jié)紅包雨活動(dòng)、“圖 2”是紅包補(bǔ)貼 、“圖 3”是紅包掛件、“圖4”是B2C的紅包發(fā)送 tab 頁(yè)。
3.2 C2C 紅包
在 C2C 紅包玩法中,用戶拍攝視頻點(diǎn)擊掛件,填寫紅包的金額和個(gè)數(shù)信息,選擇紅包的領(lǐng)取范圍后,點(diǎn)擊發(fā)送紅包會(huì)拉起收銀臺(tái),用戶支付完成后點(diǎn)擊下一步發(fā)布視頻,即可完成 C2C 紅包的發(fā)放。

?
如上圖所示,從左至左:“圖1”是C2C 紅包發(fā)送 Tab 頁(yè)??、“圖2”是支付界面、“圖3”是紅包支付后掛件展示。
3.3 紅包領(lǐng)取
B2C 和 C2C 紅包的領(lǐng)取流程都是一樣的。
用戶在抖音刷視頻遇到有視頻紅包的視頻時(shí),視頻下方有個(gè)領(lǐng)取紅包按鈕,用戶點(diǎn)擊紅包領(lǐng)取,會(huì)彈出到紅包封面。
用戶點(diǎn)擊紅包封面的開紅包后即可領(lǐng)取紅包,領(lǐng)取成功后會(huì)顯示領(lǐng)取結(jié)果彈窗,在領(lǐng)取結(jié)果中用戶可以看領(lǐng)取金額,以及跳轉(zhuǎn)到領(lǐng)取詳情頁(yè),在領(lǐng)取詳情頁(yè)中可以看到這個(gè)紅包其他用戶的領(lǐng)取手氣。

?
如上圖所示,從左至左:“圖1”是紅包視頻、“圖2”是紅包封面、“圖3”是紅包領(lǐng)取結(jié)果、“圖4”是紅包領(lǐng)取詳情。
最后,C2C視頻紅包有個(gè)推廣視頻,大家可以更形象地了解下整體操作流程:(點(diǎn)擊下方視頻可以播放 ▼)
4、技術(shù)挑戰(zhàn)
4.1 通用紅包系統(tǒng)的設(shè)計(jì)問題
在上節(jié)中有提到,本次春節(jié)活動(dòng)(指的是2022年春節(jié))需要同時(shí)支持 B2C 和 C2C 兩種類型的紅包。
這兩個(gè)類型的紅包既有一些相似的業(yè)務(wù),也有很多不同的業(yè)務(wù)。
在相同點(diǎn)上:他們都包括紅包的發(fā)放和領(lǐng)取這兩個(gè)操作。在不同點(diǎn)上:比如 B2C 的紅包發(fā)放需要通過(guò)使用補(bǔ)貼來(lái)發(fā)送,而 C2C 的紅包發(fā)放需要用戶去完成支付。B2C 的紅包用戶領(lǐng)取后需要去提現(xiàn),而 C2C 的紅包用戶領(lǐng)取后直接到零錢。
因此需要設(shè)計(jì)一個(gè)通用的紅包系統(tǒng)來(lái)支持多種紅包類型。
另外:對(duì)于紅包系統(tǒng)本身而言,除了發(fā)領(lǐng)紅包外,還涉及到一些紅包信息的查詢,以及各種狀態(tài)機(jī)的推進(jìn),這些功能模塊之間如何劃分也是需要考慮的一個(gè)點(diǎn)。
4.2 大流量補(bǔ)貼的發(fā)放處理問題
前面提到過(guò):B2C 紅包玩法會(huì)先進(jìn)行補(bǔ)貼的發(fā)放。在春節(jié)活動(dòng)期間,每場(chǎng)紅包雨都會(huì)有大量的用戶進(jìn)入?yún)⑴c,如果將這些流量直接打到數(shù)據(jù)庫(kù),將需要大量的數(shù)據(jù)庫(kù)資源。而春節(jié)期間數(shù)據(jù)庫(kù)的資源是非常稀缺的,如何減少這部分的資源消耗也是一個(gè)需要考慮的問題。
4.3 紅包領(lǐng)取方案的選型問題
在紅包業(yè)務(wù)中,領(lǐng)取是一個(gè)高頻的操作。
在領(lǐng)取方式的設(shè)計(jì)中,需要業(yè)務(wù)場(chǎng)景考慮一個(gè)紅包是否會(huì)被多個(gè)用戶同時(shí)領(lǐng)取。多個(gè)用戶同時(shí)去領(lǐng)取同一個(gè)紅包可能會(huì)導(dǎo)致熱點(diǎn)賬戶問題,成為系統(tǒng)性能的瓶頸。
解決熱點(diǎn)賬戶問題也有多個(gè)方案,我們需要結(jié)合視頻紅包的業(yè)務(wù)場(chǎng)景特點(diǎn)來(lái)選取合適的方案。
4.4 穩(wěn)定性容災(zāi)問題
在本次春節(jié)活動(dòng)中,包括 B2C 和 C2C 兩種業(yè)務(wù)流程,其中每個(gè)業(yè)務(wù)流量鏈路都依賴很多的下游服務(wù)和基礎(chǔ)服務(wù)。
在這種大型活動(dòng)中,如果出現(xiàn)黑天鵝事件時(shí),如何快速止損,減少對(duì)系統(tǒng)的整體影響,是一個(gè)必須要考慮的問題。
4.5 資金安全保證問題
在春節(jié)活動(dòng)期間,B2C 會(huì)發(fā)放大量的紅包補(bǔ)貼,如果補(bǔ)貼發(fā)生超發(fā),或者補(bǔ)貼的核銷出現(xiàn)問題,一個(gè)補(bǔ)貼被多次核銷,將會(huì)造成大量的資損。
另外 C2C 也涉及到用戶的資金流入流出,如果用戶領(lǐng)取紅包后如果發(fā)現(xiàn)錢變少了,也可能會(huì)造成大量的客訴和資損。
因此資金安全這塊需要做好充足的準(zhǔn)備。
4.6 紅包系統(tǒng)的壓測(cè)問題
在傳統(tǒng)的壓測(cè)方式中,我們一般會(huì)對(duì)某個(gè)大流量接口進(jìn)行壓測(cè)從而得到系統(tǒng)的瓶頸。
然而在紅包系統(tǒng)中,用戶的發(fā)領(lǐng)和查都是同時(shí)進(jìn)行的,并且這幾個(gè)接口之間也是相互依賴的,比如需要先發(fā)紅包,有了紅包后才能觸發(fā)多個(gè)人的領(lǐng)取,領(lǐng)取完成后才可以查看領(lǐng)取詳情。
如果使用傳統(tǒng)的單接口的壓測(cè)方式,首先 mock 數(shù)據(jù)會(huì)非常困難。和支付相應(yīng)的壓測(cè)數(shù)據(jù)因?yàn)樯婕皩?shí)名還需要特殊生成,而且單個(gè)接口單個(gè)接口的壓測(cè)很難得到系統(tǒng)的真實(shí)瓶頸。
因此如何對(duì)系統(tǒng)進(jìn)行全鏈路的壓測(cè)從而得到系統(tǒng)準(zhǔn)確的瓶頸也是我們需要解決的一個(gè)問題。
認(rèn)清了我們要面臨的技術(shù)挑戰(zhàn)和技術(shù)難題之后,接下來(lái)將分享我們是如何在實(shí)踐中一一解決這些問題的。
5、通用紅包系統(tǒng)的設(shè)計(jì)實(shí)踐
對(duì)于紅包系統(tǒng),核心包括三個(gè)操作:
1)紅包發(fā)送;
2)紅包的領(lǐng)??;
3)未領(lǐng)取紅包的退款。
另外:我們還會(huì)需要去查一些紅包的信息和領(lǐng)取的信息等。對(duì)于發(fā)送、領(lǐng)取和退款這三個(gè)核心操作,我們需要對(duì)它們的狀態(tài)進(jìn)行一個(gè)維護(hù)。同時(shí)在我們的業(yè)務(wù)場(chǎng)景中,還存在 B2C 特有的補(bǔ)貼的發(fā)放,我們也需要維護(hù)補(bǔ)貼的狀態(tài)。
在上面初步介紹紅包系統(tǒng)后,可以看到紅包的幾個(gè)功能模塊:
1)發(fā)放;
2)領(lǐng)??;
3)退款;
4)補(bǔ)貼發(fā)放;
5)各種信息查詢;
6)狀態(tài)機(jī)的維護(hù)等。
對(duì)紅包的功能進(jìn)行梳理后,我們開始對(duì)紅包的模塊進(jìn)行劃分。
模塊劃分原則:
1)功能內(nèi)聚,每個(gè)系統(tǒng)只處理一個(gè)任務(wù)(方便之后系統(tǒng)的開發(fā)和迭代,以及問題的排查);
2)API 網(wǎng)關(guān)層只進(jìn)行簡(jiǎn)單的 proxy 處理;
3)異步任務(wù)拆解;
4)讀寫分離,將紅包的核心操作和紅包的查詢分成兩個(gè)服務(wù)。
模塊劃分結(jié)果:
1)紅包網(wǎng)關(guān)服務(wù):HTTP API 網(wǎng)關(guān),對(duì)外對(duì)接客戶端和 h5,對(duì)內(nèi)封裝各個(gè)系統(tǒng) rpc 接口,限流,權(quán)限控制、降級(jí)等功能;
2)紅包核心服務(wù):主要承載紅包核心功能,包括紅包的發(fā)放、領(lǐng)取、退款,以及紅包補(bǔ)貼的發(fā)放,維護(hù)紅包狀態(tài)機(jī),紅包的狀態(tài)推進(jìn);
3)紅包查詢服務(wù):主要承載紅包查詢功能,包括紅包詳情、紅包發(fā)送狀態(tài)、紅包領(lǐng)取狀態(tài)、紅包領(lǐng)取詳情、紅包補(bǔ)貼信息;
4)紅包異步服務(wù):主要承載紅包異步任務(wù),保證狀態(tài)機(jī)的流轉(zhuǎn),包括紅包的轉(zhuǎn)賬,紅包的退款,以及紅包補(bǔ)貼的狀態(tài)推進(jìn);
5)紅包基礎(chǔ)服務(wù):主要承載紅包各個(gè)系統(tǒng)的公共調(diào)用,例如對(duì) DB,redis、tcc 的操作,公共常量和工具類,相當(dāng)于紅包的基礎(chǔ)工具包;
6)紅包對(duì)賬服務(wù):主要承載紅包和財(cái)經(jīng)的對(duì)賬邏輯,按天和財(cái)經(jīng)對(duì)賬。
最后整個(gè)視頻紅包的系統(tǒng)架構(gòu)如圖所示:

?
6、大流量補(bǔ)貼的發(fā)放處理實(shí)踐
6.1 同步獎(jiǎng)勵(lì)發(fā)放
在紅包補(bǔ)貼發(fā)放鏈路流程中,為了應(yīng)對(duì)春節(jié)的大流量,整個(gè)鏈路流程也經(jīng)歷過(guò)幾次方案的迭代。
在最初的方案設(shè)計(jì)中,我們是按照同步的補(bǔ)貼發(fā)放流程來(lái)處理的,上游鏈路調(diào)用紅包系統(tǒng)接口發(fā)券,發(fā)券成功后用戶感知到券發(fā)放成功,可以使用該券來(lái)發(fā)放紅包。
最初方案的整體流程如下圖:

上面方案的一個(gè)問題是在春節(jié)活動(dòng)期間,整個(gè)鏈路都需要能扛住活動(dòng)期間的總流量,而且最終流量都會(huì)打到數(shù)據(jù)庫(kù),而數(shù)據(jù)庫(kù)的資源在春節(jié)期間也是比較緊缺的。
6.2 異步獎(jiǎng)勵(lì)發(fā)放
為了解決同步獎(jiǎng)勵(lì)發(fā)放的問題,我們將整體流程改為通過(guò) MQ 進(jìn)行削峰,從而降低下游的流量壓力。
相當(dāng)于是從同步改為異步,用戶參與活動(dòng)后會(huì)先下發(fā)一個(gè)加密 Token 給客戶端,用于客戶端的展示以及和服務(wù)端的交互處理。
活動(dòng)異步發(fā)券方案如下圖:

?
這樣算是解決了大流量的問題,但是相應(yīng)地引入了其他的問題。。。
在最初方案中:用戶的紅包補(bǔ)貼都會(huì)先在紅包系統(tǒng)中落庫(kù),后續(xù)用戶對(duì)補(bǔ)貼的查詢和核銷我們都能在紅包數(shù)據(jù)庫(kù)中找到對(duì)應(yīng)的記錄。
但是在異步的方式中:整個(gè)補(bǔ)貼的入賬預(yù)估需要 10min,而用戶在 APP 界面感知到發(fā)券后可能馬上就會(huì)開始使用用補(bǔ)貼來(lái)發(fā)放視頻紅包,或者會(huì)去紅包掛件查看自己已經(jīng)領(lǐng)取的紅包補(bǔ)貼,而此時(shí)補(bǔ)貼還未在紅包系統(tǒng)中入賬。
6.3 最終方案
為了解決上面問題,我們對(duì)紅包補(bǔ)貼的視頻紅包發(fā)放和紅包補(bǔ)貼查詢的整個(gè)邏輯進(jìn)行了修改。即在用戶使用紅包補(bǔ)貼進(jìn)行視頻紅包發(fā)放時(shí),我們會(huì)先對(duì)該補(bǔ)貼進(jìn)行一個(gè)入庫(kù)操作,入庫(kù)成功后才可以用這個(gè)補(bǔ)貼進(jìn)行紅包發(fā)放。
另外對(duì)于查詢接口:我們無(wú)法感知到所有補(bǔ)貼是否完全入賬,因此每次查詢時(shí)我們都需要去獎(jiǎng)勵(lì)發(fā)放端查詢?nèi)康?Token 列表。同時(shí)我們還需要查詢出數(shù)據(jù)庫(kù)中用戶的補(bǔ)貼,對(duì)這兩部分?jǐn)?shù)據(jù)進(jìn)行一次 merge 操作,才能得到全量的補(bǔ)貼列表。

?
在上面的流程中:為了解決 MQ 異步會(huì)有延遲的問題,我們?cè)谟脩暨M(jìn)行請(qǐng)求時(shí)主動(dòng)地進(jìn)行入賬,而用戶主動(dòng)的操作包括使用補(bǔ)貼發(fā)放紅包和查詢補(bǔ)貼。
我們?yōu)槭裁粗辉谘a(bǔ)貼發(fā)放紅包時(shí)入賬而在查詢補(bǔ)貼時(shí)不入賬呢?
因?yàn)橛脩舻牟樵冃袨槭且粋€(gè)高頻行為,同時(shí)涉及到批量的操作,在操作 DB 前我們無(wú)法感知該補(bǔ)貼是否入賬,所以會(huì)涉及到 DB 的批量處理,甚至用戶每次來(lái)查詢時(shí)我們都需要重復(fù)這個(gè)操作,會(huì)導(dǎo)致大量的 DB 資源浪費(fèi)。
而補(bǔ)貼的發(fā)放時(shí)入賬則是一個(gè)低頻的,單個(gè)補(bǔ)貼的操作,我們只需要在用戶核銷時(shí)入賬即可,這樣可以大量減輕數(shù)據(jù)庫(kù)的壓力,節(jié)省數(shù)據(jù)庫(kù)資源。
7、紅包領(lǐng)取方案的選型實(shí)踐
在視頻紅包領(lǐng)取的技術(shù)方案中,我們也有一些方案的選擇和思考,這里和大家分享下。
7.1 悲觀鎖方案

?
如上圖所示:也是最常見的思路(我們稱為方案一),在用戶領(lǐng)取時(shí)對(duì)數(shù)據(jù)庫(kù)的紅包進(jìn)行加鎖,然后扣減金額,然后釋放鎖完成整個(gè)紅包領(lǐng)取。
這個(gè)方案的優(yōu)點(diǎn)是清晰明了,但是這種方案的問題會(huì)導(dǎo)致多個(gè)用戶同時(shí)來(lái)領(lǐng)取紅包時(shí),會(huì)造成數(shù)據(jù)庫(kù)行鎖的沖突,需要排隊(duì)等待。
當(dāng)排隊(duì)請(qǐng)求過(guò)多時(shí)會(huì)造成數(shù)據(jù)庫(kù)鏈接的浪費(fèi),影響整體系統(tǒng)的性能。同時(shí)在上游長(zhǎng)時(shí)間未收到反饋導(dǎo)致超時(shí),用戶側(cè)可能會(huì)不停重試,導(dǎo)致整體數(shù)據(jù)庫(kù)鏈接被耗盡,從而導(dǎo)致系統(tǒng)崩潰。
7.2 紅包預(yù)拆分方案
方案一的問題是多個(gè)用同時(shí)領(lǐng)取會(huì)造成鎖沖突,不過(guò)解鎖鎖沖突可以通過(guò)拆分的方式,來(lái)將鎖化成更細(xì)的粒度,從而提高單個(gè)紅包的領(lǐng)取并發(fā)量。
具體方案如下(我們稱為方案一):

?
在方案二中,對(duì)發(fā)紅包的流程進(jìn)行了一個(gè)改動(dòng),即在發(fā)紅包時(shí)會(huì)對(duì)紅包進(jìn)行一個(gè)預(yù)拆分的處理,將紅包拆成多個(gè)紅包,這樣就完成了鎖粒度的細(xì)化,在用戶領(lǐng)取紅包時(shí)從之前的爭(zhēng)搶單個(gè)紅包鎖變?yōu)楝F(xiàn)在多個(gè)紅包鎖分配。
從而在領(lǐng)取紅包時(shí)問題就變?yōu)槿绾谓o用戶分配紅包。
一種常用的思路是當(dāng)用戶請(qǐng)求領(lǐng)取紅包時(shí),通過(guò) redis 的自增方法來(lái)生成序列號(hào),該序列號(hào)即對(duì)應(yīng)該領(lǐng)取那一個(gè)紅包。但是這種方式強(qiáng)依賴 redis,在 redis 網(wǎng)絡(luò)抖動(dòng)或者 redis 服務(wù)異常時(shí),需要降級(jí)到去查詢 DB 還未領(lǐng)取的紅包來(lái)獲取序列號(hào),整體實(shí)現(xiàn)比較復(fù)雜。
7.3 最終方案
在視頻紅包的場(chǎng)景中,整個(gè)業(yè)務(wù)流程是用戶拍攝視頻發(fā)紅包,然后在視頻推薦 feed 流中刷到視頻時(shí),才會(huì)觸發(fā)領(lǐng)取。
相對(duì)于微信和飛書這種典型IM即時(shí)通訊的群聊場(chǎng)景,視頻紅包中同一個(gè)紅包的領(lǐng)取并發(fā)數(shù)并不會(huì)很高,因?yàn)橛脩羲⒁曨l的操作以及 feed 流本身就完成了流量的打散。所以對(duì)于視頻紅包來(lái)說(shuō),領(lǐng)取的并發(fā)數(shù)并不會(huì)很高。
從業(yè)務(wù)的角度來(lái)看:在需求實(shí)現(xiàn)上,我們?cè)谟脩纛I(lǐng)取完成后需要能獲取到未領(lǐng)取紅包的個(gè)數(shù)信息下發(fā)給用戶展示。方案一獲取紅包庫(kù)存很方便,而方案二獲取庫(kù)存比較麻煩。
另外從系統(tǒng)開發(fā)復(fù)雜度和容災(zāi)情況看:方案一相對(duì)來(lái)說(shuō)是一個(gè)更合適的選擇。但是方案一中的風(fēng)險(xiǎn)我們需要處理下。我們需要有其他的方式來(lái)保護(hù) DB 資源,盡量減少鎖的沖突。
具體方案如下:

?
1)紅包 redis 限流:
為盡可能少的減少 DB 鎖沖突,首先會(huì)按照紅包單號(hào)進(jìn)行限流,每次允許剩余紅包個(gè)數(shù)*1.5 的請(qǐng)求量通過(guò)。
被限流返回特殊錯(cuò)誤碼,前端最多輪訓(xùn) 10 次,在請(qǐng)求量過(guò)多的情況下通過(guò)這種方式來(lái)慢慢處理。
2)內(nèi)存排隊(duì):
除了 redis 限流外,為了減少 DB 鎖,我們?cè)陬I(lǐng)取流程中加個(gè)一個(gè)紅包內(nèi)存鎖。
對(duì)于單個(gè)紅包,只有獲取到內(nèi)存鎖的請(qǐng)求才能繼續(xù)去請(qǐng)求 DB,從而將 DB 鎖的沖突遷移到內(nèi)存中提前處理,而內(nèi)存資源相對(duì)于 DB 資源來(lái)說(shuō)是非常廉價(jià)的,在請(qǐng)求量過(guò)大時(shí),我們可以水平擴(kuò)容。
為了實(shí)現(xiàn)內(nèi)存鎖,我們進(jìn)行了幾個(gè)改動(dòng)。
首先:需要保證同一個(gè)紅包請(qǐng)求能打到同一個(gè) tce 實(shí)例上,這里我們對(duì)網(wǎng)關(guān)層路由進(jìn)行了調(diào)整,在網(wǎng)關(guān)層調(diào)用下游服務(wù)時(shí),會(huì)按照紅包單號(hào)進(jìn)行路由策略,保證同一單號(hào)的請(qǐng)求打到同一個(gè)實(shí)例上。
另外:我們?cè)诩t包系統(tǒng)的 core 服務(wù)中基于 channel 實(shí)現(xiàn)了一套內(nèi)存鎖,在領(lǐng)取完成后會(huì)釋放該紅包對(duì)應(yīng)的內(nèi)存鎖。
最后:為了防止鎖的內(nèi)存占用過(guò)大或者未及時(shí)釋放,我們起了一個(gè)定時(shí)任務(wù)去定期地處理。
3)轉(zhuǎn)賬異步化:
從接口耗時(shí)來(lái)看:轉(zhuǎn)賬是一個(gè)耗時(shí)較長(zhǎng)的操作,本身涉及和第三方支付機(jī)構(gòu)交互,會(huì)有跨機(jī)房請(qǐng)求,響應(yīng)延時(shí)較長(zhǎng)。將轉(zhuǎn)賬異步化可以降低領(lǐng)取紅包接口的時(shí)延,提高服務(wù)性能和用戶體驗(yàn)。
從用戶感知來(lái)看:用戶更關(guān)注的是領(lǐng)取紅包的點(diǎn)擊開后是否領(lǐng)取成功,至于余額是否同步到賬用戶其實(shí)感知沒那么強(qiáng)烈。
另外:轉(zhuǎn)賬本身也是有一個(gè)轉(zhuǎn)賬中到轉(zhuǎn)賬成功的過(guò)程,將轉(zhuǎn)賬異步化對(duì)于用戶的感知基本沒有影響。
8、穩(wěn)定性容災(zāi)實(shí)踐
8.1 概述
整個(gè)紅包系統(tǒng)的容災(zāi),我們主要從以下3個(gè)方式來(lái)進(jìn)行的:
1)接口限流;
2)業(yè)務(wù)降級(jí);
3)多重機(jī)制保證狀態(tài)機(jī)的推進(jìn)。
如下圖所示:

?
下面對(duì)這幾個(gè)方式分別介紹下。
8.2 接口限流
接口限流是一種常見的容災(zāi)方式,用于保護(hù)系統(tǒng)只處理承受范圍內(nèi)的請(qǐng)求,防止外部請(qǐng)求過(guò)大將系統(tǒng)打崩。
在進(jìn)行接口限流前,我們首先需要和上下游以及產(chǎn)品溝通得到一個(gè)預(yù)估的紅包發(fā)放和領(lǐng)取量,然后根據(jù)發(fā)放和領(lǐng)取量進(jìn)行分模塊地全鏈路的大盤流量梳理。
下面是當(dāng)時(shí)我們梳理的一個(gè) b2c 全鏈路的請(qǐng)求量:

?
有個(gè)各個(gè)模塊的請(qǐng)求量后,匯總之后就可以得到各個(gè)接口,紅包系統(tǒng)各個(gè)服務(wù)以及下游依賴的各個(gè)服務(wù)的流量請(qǐng)求,這個(gè)時(shí)候再做限流就比較方便了。
8.3 業(yè)務(wù)降級(jí)
8.3.1)核心依賴降級(jí):
在春節(jié)活動(dòng)期間,紅包系統(tǒng)整個(gè)鏈路依賴的服務(wù)有很多,這些下游的鏈路依賴可以分為核心依賴和非核心依賴。
當(dāng)下游核心服務(wù)異常時(shí),可能某一個(gè)鏈路就不可用,此時(shí)可以在 API 層直接降級(jí)返回一個(gè)比較友好的文案提示,等下游服務(wù)恢復(fù)后再放開。
比如在 C2C 的紅包發(fā)送流程中,用戶需要完成支付才可以發(fā)紅包,如果財(cái)經(jīng)的支付流程異?;蛘咧Ц冻晒顟B(tài)長(zhǎng)時(shí)間未完成,會(huì)造成用戶支付后紅包發(fā)送不成功,也會(huì)導(dǎo)致前端來(lái)不停的輪訓(xùn)查詢紅包狀態(tài),導(dǎo)致請(qǐng)求量陡增,造成服務(wù)壓力,甚至影響 B2C 的紅包發(fā)放和查詢。
此時(shí)可以通過(guò)接口降級(jí)的方式,將 C2C 的紅包發(fā)放降級(jí)返回,減少服務(wù)壓力,同時(shí)降低對(duì)其他業(yè)務(wù)邏輯的影響。
8.3.2)非核心依賴降級(jí):
除核心依賴外,紅包系統(tǒng)還有一些非核心的下游依賴,對(duì)于這些依賴,如果服務(wù)出現(xiàn)異常,我們可以降低用戶部分體驗(yàn)的方式來(lái)保證服務(wù)的可用。
比如在前面我們提到的,用戶在發(fā) B2C 紅包前需要先獲取所有可用的紅包補(bǔ)貼,我們會(huì)去獎(jiǎng)勵(lì)發(fā)放端查詢到所有的 Token 列表,然后查詢我們自己的 DB,然后進(jìn)行 merge 返回。
如果獲取 Token 列表的接口異常時(shí),我們可以降級(jí)只返回我們自己 DB 中的補(bǔ)貼數(shù)據(jù),這樣可以保證用戶在這種情況下還可以進(jìn)行紅包的發(fā)放,只影響部分補(bǔ)貼的展示,而不是影響整個(gè)紅包發(fā)送鏈路。
8.4 多重機(jī)制保證狀態(tài)機(jī)的推進(jìn)
在紅包系統(tǒng)中,如果某個(gè)訂單長(zhǎng)時(shí)間未到終態(tài),比如用戶領(lǐng)取紅包后長(zhǎng)時(shí)間未到賬,或者用戶 C2C 紅包未領(lǐng)取長(zhǎng)時(shí)間未給用戶退款都有可能造成用戶的客訴。
因此需要及時(shí)準(zhǔn)確地保證系統(tǒng)中各個(gè)訂單的狀態(tài)能推到終態(tài)。
這里我們有幾種方式去保證。
首先是回調(diào),在依賴方系統(tǒng)訂單處理完后會(huì)及時(shí)地通知給紅包系統(tǒng),這種方式也是最及時(shí)的一種方式。
但是只依賴回調(diào)可能會(huì)出現(xiàn)依賴方異?;蛘呔W(wǎng)絡(luò)抖動(dòng)導(dǎo)致回調(diào)丟失,此時(shí)我們?cè)诩t包的各個(gè)階段都會(huì)給紅包系統(tǒng)發(fā)一個(gè) mq,間隔一定的時(shí)間去消費(fèi) mq 主動(dòng)查詢依賴方的訂單狀態(tài)進(jìn)行更新。
最后,我們對(duì)每個(gè)狀態(tài)機(jī)都會(huì)有一個(gè)定時(shí)任務(wù)用于兜底,在定時(shí)任務(wù)多次執(zhí)行仍未到終態(tài)的會(huì) lark 通知,及時(shí)人工介入發(fā)現(xiàn)問題。
9、資金安全保證實(shí)踐
9.1 交易冪等
在編程中,冪等指任意多次執(zhí)行一個(gè)請(qǐng)求所產(chǎn)生的影響與一次執(zhí)行的影響相同。在資金安全中,通過(guò)訂單號(hào)來(lái)進(jìn)行相應(yīng)的冪等邏輯處理可以防止資損的發(fā)生。
具體來(lái)說(shuō):在紅包系統(tǒng)中,在紅包的發(fā)放、領(lǐng)取和退款中,我們都通過(guò)訂單號(hào)唯一鍵來(lái)保證接口冪等。
另外:紅包系統(tǒng)的補(bǔ)貼發(fā)放接口是冪等的,外部同一個(gè)單號(hào)多次請(qǐng)求發(fā)放補(bǔ)貼,我們需要保證只會(huì)發(fā)一張券。
實(shí)現(xiàn)冪等的方案很多:包括有通過(guò)數(shù)據(jù)庫(kù)或者 redis 來(lái)實(shí)現(xiàn)冪等的。最可靠的就是通過(guò)數(shù)據(jù)庫(kù)的唯一鍵沖突來(lái)實(shí)現(xiàn),但是這種方式在數(shù)據(jù)庫(kù)存在分片實(shí)例時(shí)會(huì)引入一些額外的問題。
這里:我們就補(bǔ)貼的發(fā)放來(lái)簡(jiǎn)單介紹下,在業(yè)務(wù)系統(tǒng)的設(shè)計(jì)中,我們是按照 uid 分片的方式來(lái)建立業(yè)務(wù)的數(shù)據(jù)庫(kù)表,這就導(dǎo)致補(bǔ)貼的分片鍵是 uid,雖然我們也設(shè)置了紅包的補(bǔ)貼單號(hào)作為唯一鍵。
但是其中存在一個(gè)風(fēng)險(xiǎn)就是如果上游的系統(tǒng)調(diào)用補(bǔ)貼發(fā)放時(shí),同一個(gè)外部單號(hào)更換了 uid,就可能會(huì)導(dǎo)致兩個(gè)請(qǐng)求分別打到不同的數(shù)據(jù)庫(kù)實(shí)例上,導(dǎo)致唯一索引失效,造成資損。
為了解決這個(gè)問題,我們又額外的引入一個(gè)以補(bǔ)貼發(fā)放外部單號(hào)作為分片鍵的數(shù)據(jù)庫(kù)來(lái)解決這個(gè)風(fēng)險(xiǎn)。

?
9.2 B2C 紅包核對(duì)
除了在開發(fā)過(guò)程的系統(tǒng)設(shè)計(jì)上進(jìn)行相應(yīng)的資金安全考慮,我們還需要通過(guò)對(duì)賬的方式來(lái)校驗(yàn)我們的系統(tǒng)是否有資金安全問題。
在 B2C 鏈路中,整個(gè)鏈路主要是從補(bǔ)貼發(fā)放到紅包領(lǐng)取,我們對(duì)這幾個(gè)鏈路的上下游的數(shù)據(jù)都進(jìn)行相應(yīng)的小時(shí)計(jì) hive 對(duì)賬。

?
?
9.3 C2C 紅包核對(duì)
在 C2C 鏈路中,整個(gè)主要從用戶發(fā)起支付,到用戶領(lǐng)取轉(zhuǎn)賬以及最后紅包過(guò)期退款。
在支付、轉(zhuǎn)賬、退款這三個(gè)流程都需要進(jìn)行相應(yīng)的核對(duì)。
同時(shí):還需要保證用戶的紅包發(fā)放金額大于等于紅包轉(zhuǎn)賬金額+紅包退款金額,這里大于等于是因?yàn)榧t包從發(fā)放成功到退款成功整個(gè)周期會(huì)在 24h 以上。
另外:可能存在轉(zhuǎn)賬在途的這種訂導(dǎo)致會(huì)有多筆退款單,如果要求嚴(yán)格等于的話具體對(duì)賬時(shí)機(jī)沒法控制。

?
10、紅包系統(tǒng)的壓測(cè)實(shí)踐
10.1 概述
前面提到過(guò),紅包系統(tǒng)的鏈路包含有多個(gè)接口,發(fā)領(lǐng)查等,需要模擬用戶的真實(shí)行為來(lái)進(jìn)行壓測(cè)才能得到系統(tǒng)的真實(shí)性能。這里我們使用了壓測(cè)平臺(tái)的腳本壓測(cè)方式來(lái)進(jìn)行壓測(cè)。
首先:需要對(duì)整個(gè)壓測(cè)鏈路整個(gè)改造,和上下游溝通是否可以壓測(cè),不能壓測(cè)的需要進(jìn)行相應(yīng)的 mock 處理。
另外:對(duì)于存儲(chǔ)服務(wù),數(shù)據(jù)庫(kù),redis 和 mq 都要確保壓測(cè)標(biāo)的正確傳遞,否則可能會(huì)影響到線上。
改造完壓測(cè)鏈路后,需要構(gòu)造相應(yīng)的壓測(cè)腳本,對(duì)于 B2C 和 C2C 分為兩個(gè)腳本。
10.2 B2C 紅包鏈路壓測(cè)
?

?
上面是 B2C 壓測(cè)的整個(gè)鏈路,首先是補(bǔ)貼的發(fā)放,然后通過(guò)查詢補(bǔ)貼,通過(guò)補(bǔ)貼來(lái)發(fā)放紅包,為了模擬多人來(lái)領(lǐng)取的情況,我們起了多個(gè) goroutinue 來(lái)并發(fā)的領(lǐng)取紅包。
10.3 C2C 紅包鏈路壓測(cè)
?
C2C 紅包因?yàn)樯婕暗街Ц断嚓P(guān)的操作,整個(gè)鏈路又是另外一套流程,因此對(duì)于 C2C 也需要有一個(gè)單獨(dú)的腳本。
在壓測(cè)流程中,因?yàn)樯婕暗酵獠肯到y(tǒng)的依賴,如果等待全鏈路 OK 時(shí)再一起壓測(cè)可能會(huì)導(dǎo)致一些未知的問題出現(xiàn)。
因此我們需要自己壓測(cè)沒問題后再開始全鏈路一起壓測(cè),在圖中和支付相關(guān)的藍(lán)色模塊我們都添加了相應(yīng)的 mock 開關(guān),來(lái)控制壓測(cè)的結(jié)果。
在 mock 開關(guān)打開時(shí),會(huì)直接構(gòu)造一個(gè)結(jié)果返回,在 mock 開關(guān)關(guān)閉時(shí),會(huì)正常地去請(qǐng)求財(cái)經(jīng)獲取結(jié)果。
11、后續(xù)規(guī)劃(服務(wù) Set 化)
?
在前面提到的系統(tǒng)容災(zāi)中,如果紅包核心服務(wù)改掉,或者數(shù)據(jù)庫(kù) DB 主機(jī)房掛掉,將影響所有的用戶。此時(shí)只能降級(jí)返回,整個(gè)系統(tǒng)無(wú)法快速切換和恢復(fù)。
后續(xù)考慮將服務(wù)改為 set 化的架構(gòu)。
即將服務(wù) Server 和對(duì)應(yīng)的存儲(chǔ)劃分為一個(gè)單獨(dú)的 Set,每個(gè) Set 只處理對(duì)應(yīng)劃分單元內(nèi)的流量,同時(shí)多個(gè)單元之間實(shí)現(xiàn)流量拆分和故障隔離,以及 Set 之間數(shù)據(jù)備份。
這樣后續(xù)在某個(gè)單元異常時(shí),可以及時(shí)將對(duì)應(yīng)單元的流量切到備份單元中。
12、更多資料
[1]?一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(上篇):整體架構(gòu)、服務(wù)拆分等
[2]?一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(下篇):可靠性、有序性、弱網(wǎng)優(yōu)化等
[3]?從新手到專家:如何設(shè)計(jì)一套億級(jí)消息量的分布式IM系統(tǒng)
[4]?阿里技術(shù)分享:電商IM消息平臺(tái),在群聊、直播場(chǎng)景下的技術(shù)實(shí)踐
[5]?一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐
[6]?一套海量在線用戶的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)
[7]?一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案
[8]?新手入門一篇就夠:從零開發(fā)移動(dòng)端IM
(本文已同步發(fā)布于:http://www.52im.net/thread-3945-1-1.html)