关于微信红包的架构思考
關于微信紅包的架構思考
微信紅包的架構實現是前段時間技術圈里很熱門的一個話題,這是一個非常典型的大訪問高并發場景。至于如何實現,答案并不是唯一的,對這個問題的思考其實反映了工程師的架構設計功底。所以我也談一談我的想法。
首先任何架構設計離不開對業務的理解和認識,架構設計并不是憑空存在的,一定是基于業務場景并且服務業務場景的。所以我先分析一下微信紅包的業務場景。
*** 需要特別說一點,本文中的架構設計不一定是官方的實現方式,只是一種分析,是基于我目前的技術能力的一種思考 ***
業務場景分析
根據微信紅包的操作,可以把紅包的業務場景分為發放紅包,搶紅包和打開紅包三個步驟。
PS:我覺得微信紅包的架構討論之所以非常火爆,一個重要原因就是微信紅包的業務場景大家都很熟悉
發放紅包:
- 發放者設置紅包金額,紅包數量等屬性
- 從發放者賬戶扣除紅包金額
- 生成紅包,并且發送搶紅包的鏈接
搶紅包:
- 用戶點擊紅包鏈接
打開紅包:
- 顯示用戶搶到的紅包金額
- 剩余紅包數量-1,紅包剩余金額改變
- 搶到紅包的用戶賬戶金額增加
注意,以上均為原子操作
根據業務場景,可以知道紅包的主要屬性有:
- 紅包金額:始終不變
- 紅包總數量:始終不變
- 紅包剩余金額:每次有人領取紅包,該屬性變化
- 紅包剩余數量:每次有人領取紅包,該屬性-1
- 搶到紅包的用戶以及搶到的金額:用于顯示手氣排行,并且防止重復搶紅包
- 過期時間:如果到過期紅包仍未搶完,返回發放者賬戶
架構分析
單純實現微信紅包的功能并不復雜,其難點在于如何處理高訪問和并發,當發出一個紅包之后,只有少數人能夠搶到,而大部分的請求都屬于無效請求,如果讓這部分請求落到數據庫或者在服務端經過復雜處理,那么以微信的用戶體量,后果是災難性的。
從這個角度來說,微信紅包和電商的秒殺業務有幾分相似
處理類似業務最常見的手段就是請求過濾和添加緩存(cache),當然,至于服務器集群,負載均衡,讀寫分離這些基礎架構,由于已經成了大型網站的標配,默認已經存在。
其中,請求過濾是只允許少數符合條件的請求走到最后,把大量不符合條件的請求擋在外面,這個非常關鍵。而采用緩存技術,原因有兩個,一是緩存的訪問速度快,使用緩存可以有效提高吞吐速度,二是紅包發放相對來說是一個臨時性的東西,故而可以放在緩存里面。
業務上需要注意的地方
- 防止用戶重復搶紅包
- 應對redis宕機的風險,目前一般是采用sentinel機制
架構設計
緩存我們默認使用redis,數據庫默認使用mysql
新建紅包的時候,生成唯一紅包id,設置紅包屬性,在mysql中添加記錄,表結構大致如下
|紅包id|發放者uid|紅包總金額|紅包總數量|紅包剩余金額|紅包剩余數量|搶到紅包的用戶列表json|過期時間|創建時間|
 |---|:----|:---|---|----|---|---|----|---|--|--|:---|:----|:---|---|----|---|---|----|---|--
同時,在redis里添加:
- 搶紅包請求隊列(隊列)
- 打開紅包請求隊列(隊列)
- 紅包信息,包含剩余紅包數量和剩余金額(鍵值對)
- 已經搶到紅包的用戶信息(有序集合)
當搶紅包的時候,
 做以下判斷:
- 剩余紅包數量是否大于0
- 該用戶是否在已經搶到紅包的用戶集合里面
 通過判斷的請求添加進請求隊列,否則返回已經搶完的標示,這樣下一步用戶打開紅包的時候,甚至可以不發送請求直接顯示紅包已經搶完,過濾無效請求
于此同時,另外有進程從請求隊列里取出搶紅包請求,生成token標示,返回給搶紅包者
當用戶打開紅包的時候,傳入該標示,放進打開紅包請求隊列,后端消費者進程從隊列中取出打開紅包請求,判斷紅包數量是否大于0,以及用戶傳來的打開紅包token與紅包id是否合法,參數檢驗之后,生成紅包金額,調用余額接口處理,并且更新redis中紅包剩余數量,紅包剩余金額
最后異步方式更新數據庫
紅包算法
關于紅包算法,網上說的很多了,這里就不啰嗦了,金額是拆的時候實時算出來,不是預先分配的,采用的是純內存計算,不需要預算空間存儲,具體的算法是在在0.01和剩余平均值的2倍之間隨機生成一個金額
效果
比如現在發了一個紅包,分為5份。有100個人搶。
可能有10個人成功進入請求隊列(搶到紅包和打開紅包之間有一定間隔,所以先搶到的不一定先打開),拿到了打開紅包token,其余90個人拿到的是紅包已經搶完的token,那么當打開紅包的時候,這90個人甚至都不需要發送請求,直接顯示紅包已搶完
這10個人打開紅包的時候,其中5個人搶到,剩下的5個也顯示紅包已經搶完
本來5個紅包100個人搶,會請求201次(生成紅包1次+搶紅包100次+打開紅包100次)
 那么這種情況下,只需要111次(生成紅包1次+搶紅包100次+打開紅包10次),并且,其中90次請求很快就返回,大大減輕服務端壓力
需要注意的是
- 如何保證原子性操作
- 如何提高可用性,主要是防止redis宕機
總結
衡量一個架構設計的標準主要是性能,擴展性,伸縮性,可用性,安全性等指標,本例中,由于紅包業務是一個很具體的業務,并非通用服務,所以不討論擴展性。
- 性能角度,由于采用了緩存以及請求過濾等手段,性能肯定比常規實現大大提高
- 伸縮性角度,應該采用服務器集群,redis集群,mysql集群的方式部署,注意redis集群中的一致性hash的實現
- 可用性角度,本例中決定可用性的關鍵還是在于redis集群是否能夠保證高可用,所以sentinel機制必不可少,甚至還應該加上其他的各種檢測節點,替換節點方案,以保證高可用。而且需要考慮極端情況,例如緩存被擊穿導致mysql壓力過大的情況下,如何確保服務不宕機。
- 安全性角度,紅包涉及資金來往,所以需要格外注意安全性。紅包業務中,是通過調用接口來實現資金往來,故賬目平衡的風險不是由紅包系統來承擔而是由紅包所調用的接口保證的,但紅包系統也可能存在以下幾種安全隱患,單一用戶重復搶紅包,所有用戶搶的的紅包金額總數大于發放者設置的紅包金額等。所以其關鍵在于確保搶紅包,打開紅包均為一次原子性操作。如有必要,甚至可以通過加鎖等方式實現,但需要考慮加鎖對于性能上的影響
 作者:cuihang
 鏈接:https://www.jianshu.com/p/63f238b04c59
 來源:簡書
 簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。
總結
以上是生活随笔為你收集整理的关于微信红包的架构思考的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 谈谈我对服务熔断、服务降级的理解
- 下一篇: 微信红包的支撑架构原理是什么?
