紅包功能的設計實現是一個很有趣的話題,主要的功能是P個人搶總金額M的N個紅包,滿足先搶的N個人能搶到紅包。如果這是一個leetcode的算法題目難度應該是easy,只要保證Ni搶到的金額區間在[0.01,2倍剩余金額平均值)就能ac。
將算法帶入到真實的工程實現,問題就要復雜得多,如果達到微信的量級,明顯要考慮的有以下幾點。
- 拆紅包
- 高並發讀
- 並發寫
- 網絡流量峰值
- 對賬
- 降級
- 故障恢復
拆紅包
拆紅包有預拆包和實時拆包2種策略
預拆包策略
預拆包的策略在發紅包時將金額M的紅包拆分成N份,將分配好的結果放入內存隊列或者cache,通過incr操作在用戶搶紅包時分配預算好的紅包slot,預算的策略可以避免對共享資源的操作,減少了鎖競爭,服務本身是無狀態的,設計和實現相對簡單,伸縮性較好。劣勢是需要額外的存儲空間,如果存在大量活躍紅包或者紅包份數很多時會增加成本。
實時拆包
實時拆包的策略在用戶搶紅包時實時拆包計算金額,這樣只需要保存剩余紅包數量和金額,不需要額外保存每個預拆包的紅包金額。使用預拆包的策略會面臨並發寫的問題,如果多個拆紅包的請求同時執行會導致數據不一致引起超發的問題,可以使用CAS操作實現樂觀鎖保證並發拆包不會出現問題。
高並發讀
應對高並發讀的通常思路是業務層攔截過濾無效請求,使用有效的緩存。可以使用Cache層decr功能記錄請求紅包的用戶數,當decr到0后就攔截后面的請求直接返回,對DAO層也要增加相應的緩存減少數據庫的壓力。
並發寫
應對並發寫的通常思路是串行化和樂觀鎖。在用戶搶紅包時實時拆包計算金額,每搶到一個紅包,就cas更新剩余金額和紅包個數,同時在DB中記錄憑證,考慮到DB的寫入壓力,需要做分庫分表,冷熱分離。
網絡流量峰值
大量用戶同時搶紅包是否會造成網絡擁塞,發紅包和搶紅包最好在同一個IDC。
對賬
考慮到拆紅包憑證和入賬是異步的2套系統,以及出現故障的可能,需要定時對賬保證數據的一致性。
降級
在cache故障時有限流的使用DB進行服務,在資源緊張的時候關閉掉非核心流程,在實時入賬請求量過大時,延遲批量入賬。
Reference:
https://www.zybuluo.com/yulin718/note/93148