search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

如何設計一個小而美的秒殺系統?

作者|劉鵬編輯|Gary

現如今,春節搶紅包的活動已經逐漸變成大家過年的新風俗。親朋好友的相互饋贈,微信、微博、支付寶等各大平台種類繁多的紅包讓大家收到手軟。雞年春節,鏈家也想給15萬的全國員工包個大紅包,於是我們構建了一套旨在支撐10萬每秒請求峰值的搶紅包系統。經實踐證明,春節期間我們成功的為所有的小夥伴提供了高可靠的服務,紅包總發放量近百萬,搶紅包的峰值流量達到3萬/秒,最快的一輪搶紅包活動3秒鐘所有紅包全部搶完,系統運行0故障。

紅包系統,類似於電商平台的秒殺系統,本質上都是在一個很短的時間內面對巨大的請求流量,將有限的庫存商品分發出去,並完成交易操作。比如12306搶票,庫存的火車票是有限的,但瞬時的流量非常大,且都是在請求相同的資源,這裡面資料庫的併發讀寫衝突以及資源的鎖請求衝突非常嚴重。就我們實現這樣一個紅包系統本身來說,面臨著如下的一些挑戰:

首先,到活動整點時刻,我們有15萬員工在固定時間點同時湧入系統搶某輪紅包,瞬間的流量是很大的,而目前我們整個鏈路上的系統和服務基礎設施,都沒有承受過如此高的吞吐量,要在短時間內實現業務需求,在技術上的風險較大。

其次,公司是第一次開展這樣的活動,我們很難預知大家參與活動的情況,極端情況下可能會出現某輪紅包沒搶完,需要合併到下輪接著發放。這就要求系統有一個動態的紅包發放策略和預算控制,其中涉及到的動態計算會是個較大的問題(這也是為系統高吞吐服務),實際的系統實現中我們採用了一些預處理機制。

最後,這個系統是為了春節的慶祝活動而研發的定製系統,且只上線運行一次,這意味著我們無法積累經驗去對服務做持續的優化。並且相關的配套環境沒有經過實際運行檢驗,缺少參考指標,系統的薄弱環節發現的難度大。所以必須要追求設計至簡,盡量減少對環境的依賴(數據路徑越長,出問題的環節越多),並且實現高可伸縮性,需要盡一切努力保證可靠性,即使有某環節失誤,系統依然能夠保障核心的用戶體驗正常。

系統設計

系統架構圖如圖所示。所有的靜態資源提前部署在了第三方的CDN服務上,系統的核心功能主要劃分到接入層和核心邏輯系統中,各自部署為集群模式並且獨立。接入層主要是對用戶身份鑒權和結果緩存,核心繫統重點關注紅包的分發,紅色實線的模塊是核心邏輯,為了保障其可靠性,我們做了包括數據預處理、水平分庫、多級緩存、精簡RPC調用、過載保護等多項設計優化,並且在原生容器、MySQL等服務基礎設施上針對特殊的業務場景做了優化,後面將為讀者一一道來。

紅包本身的信息通過預處理資源介面獲取。運行中用戶和紅包的映射關係動態生成。底層使用內部開發的DB中間件在MySQL資料庫集群上做紅包發放結果持久化,以供非同步支付紅包金額到用戶賬戶使用。整個系統的絕大部分模塊都有性能和保活監控。

優化方案

優化方案中最重要的目標是保障關鍵流程在應對大量請求時穩定運行,這需要很高的系統可用性。所以,業務流程和數據流程要盡量精簡,減少容易出錯的環節。此外,緩存、DB、網路、容器環境,任何一個部分都要假設可能會短時出現故障,要有處理預案。針對以上的目標難點,我們總結了如下的實踐經驗。

1. 數據預處理

紅包本身的屬性信息(金額,狀態,祝福語,發放策略),我們結合活動預案要求,使用一定的演算法提前生成好所有的信息,數據總的空間不是很大。為了最大化提升性能,這些紅包數據,我們事先存儲在資料庫中,然後在容器載入服務啟動時,直接載入到本地緩存中當作只讀數據。另外,我們的員工信息,我們也做了一定的裁剪,最基本的信息也和紅包數據一樣,預先生成,服務啟動時載入。

此外,我們的活動頁面,有很多視頻和圖片資源,如果這麼多的用戶從我們的網關實時訪問,很可能我們的帶寬直接就被這些大流量的請求佔滿了,用戶體驗可想而知。最後這些靜態資源,我們都部署在了CDN上,通過數據預熱的方式加速客戶端的訪問速度,網關的流量主要是來自於搶紅包期間的小數據請求。

2. 精簡RPC調用

通常的服務請求流程,是在接入層訪問用戶中心進行用戶鑒權,然後轉發請求到後端服務,後端服務根據業務邏輯調用其他上游服務,並且查詢資料庫資源,再更新服務/資料庫的數據。每一次RPC調用都會有額外的開銷,所以,比如上一點所說的預載入,使得系統在運行期間每個節點都有全量的查詢數據可在本地訪問,搶紅包的核心流程就被簡化為了生成紅包和人的映射關係,以及發放紅包的後續操作。再比如,我們採用了非同步拉的方式進行紅包發放到賬,用戶搶紅包的請求不再經過發放這一步,只記錄關係,性能得到進一步提升。

實際上有些做法的可伸縮性是極強的。例如紅包數據的預生成信息,在當時的場景下我們是能夠作為本地內存緩存加速訪問的。當紅包數據量很大的時候,在每個服務節點上使用本地資料庫,或者本地數據文件,甚至是本地Redis/MC緩存服務,都是可以保證空間足夠的,並且還有額外的好處,越少的RPC,越少的服務抖動,只需要關注系統本身的健壯性即可,不需要考慮外部系統QoS。

3. 搶紅包的併發請求處理

春節整點時刻,同一個紅包會被成千上萬的人同時請求,如何控制併發請求,確保紅包會且僅會被一個用戶搶到?

做法一,使用加鎖操作先佔有鎖資源,再佔有紅包。

可以使用分散式全局鎖的方式(各種分散式鎖組件或者資料庫鎖),申請lock該紅包資源成功后再做後續操作。優點是,不會出現臟數據問題,某一個時刻只有一個應用線程持有lock,紅包只會被至多一個用戶搶到,數據一致性有保障。缺點是,所有請求同一時刻都在搶紅包A,下一個時刻又都在搶紅包B,並且只有一個搶成功,其他都失敗,效率很低。

做法二,單獨開發請求排隊調度模塊。

排隊模塊接收用戶的搶紅包請求,以FIFO模式保存下來,調度模塊負責FIFO隊列的動態調度,一旦有空閑資源,便從隊列頭部把用戶的訪問請求取出后交給真正提供服務的模塊處理。優點是,具有中心節點的統一資源管理,對系統的可控性強,可深度定製。缺點是,所有請求流量都會有中心節點參與,效率必然會比分散式無中心繫統低,並且,中心節點也很容易成為整個系統的性能瓶頸。

做法三,巧用Redis特性,使其成為分散式序號生成器。(我們最終採用的做法)。

前文已經提到,紅包系統所使用的紅包數據都是預先生成好的,我們使用數字ID來標識,這個ID是全局唯一的,所有圍繞紅包的操作都使用這個ID作為數據的關聯項。在實際的請求流量過來時,我們採用了「分組」處理流量的方式,如下圖所示。

訪問請求被LB分發到每個分組,一個分組包含若干台應用容器、獨立的資料庫和Redis節點。Redis節點內存儲的是這個分組可以分發的紅包ID號段,利用Redis單進程的自減數值特性實現分散式紅包ID生成器,服務通過此獲取當前拆到的紅包。落地數據都持久化在獨立的資料庫中,相當於是做了水平分庫。某個分組內處理的請求,只會訪問分組內部的Redis和資料庫,和其他分組隔離開。

分組的方式使得整個系統實現了高內聚,低耦合的原則,能將數據流量分而治之,提升了系統的可伸縮性,當面臨更大流量的需求時,通過線性擴容的方法,即可應對。並且當單個節點出現故障時,影響面能夠控制在單個分組內部,系統也就具有了較好的隔離性。

4. 系統容量評估,藉助數據優化,過載保護

由於是首次開展活動,我們缺乏實際的運營數據,一切都是摸著石頭過河。所以從項目伊始,我們便強調對系統各個層次的預估,既包括了活動參與人數、每個功能feature用戶的高峰流量、後端請求的峰值、緩存系統請求峰值和資料庫讀寫請求峰值等,還包括了整個業務流程和服務基礎設施中潛在的薄弱環節。後者的難度更大因為很難量化。此前我們連超大流量的全鏈路性能壓測工具都較缺乏,所以還是有很多實踐的困難的。

在這裡內心真誠的感謝開源社區的力量,在我們制定完系統的性能指標參考值后,藉助如wrk等優秀的開源工具,我們在有限的資源里實現了對整個系統的端到端全鏈路壓測。實測中,我們的核心介面在單個容器上可以達到20,000以上的QPS,整個服務集群在110,000以上的QPS壓力下依然能穩定工作。

正是一次次的全鏈路壓測參考指標,幫助我們了解了性能的基準,並以此做了代碼設計層面、容器層面、JVM層面、MySQL資料庫層面、緩存集群層面的種種優化,極大的提升了系統的可用性。具體做法限於篇幅不在此贅述,有興趣的讀者歡迎交流。

此外,為了確保線上有超預估流量時系統穩定,我們做了過載保護。超過性能上限閾值的流量,系統會快速返回特定的頁面結果,將此部分流量清理掉,保障已經接受的有效流量可以正常處理。

5. 完善監控

系統在線上運行過程中,我們很需要對其實時的運行情況獲取信息,以便能夠對出現的問題進行排查定位,及時採取措施。所以我們必須有一套有效的監控系統,能夠幫我們觀測到關鍵的指標。在實際的操作層面,我們主要關注了如下指標:

服務介面的性能指標

藉助系統的請求日誌,觀測服務介面的QPS,介面總的實時響應時間。同時通過HTTP的狀態碼觀測服務的語義層面的可用性。

系統健康度

結合總的性能指標以及各個模塊應用層的性能日誌,包括模塊介面返回耗時,和應用層日誌的邏輯錯誤日誌等,判斷系統的健康度。

整體的網路狀況

盡量觀測每個點到點之間的網路狀態,包括應用伺服器的網卡流量、Redis節點、資料庫節點的流量,以及入口帶寬的佔用情況。如果某條線路出現過高流量,便可及時採取擴容等措施緩解。

服務基礎設施

應用伺服器的CPU、Memory、磁碟IO狀況,緩存節點和資料庫的相應的數據,以及他們的連接數、連接時間、資源消耗檢測數據,及時的去發現資源不足的預警信息。

對於關鍵的數據指標,在超過預估時制定的閾值時,還需要監控系統能夠實時的通過手機和郵件實時通知的方式讓相關人員知道。另外,我們在系統中還做了若干邏輯開關,當某些資源出現問題並且自動降級和過載保護模塊失去效果時,我們可以根據狀況直接人工介入,在服務不停機的前提前通過手動觸發邏輯開關改變系統邏輯,達到快速響應故障,讓服務儘快恢復穩定的目的。

6. 服務降級

當伺服器壓力劇增的時候,如果某些依賴的服務設施或者基礎組件超出了工作負荷能力,發生了故障,這時候極其需要根據當前的業務運行情況對系統服務進行有策略的降級運行措施,使得核心的業務流程能夠順利進行,並且減輕伺服器資源的壓力,最好在壓力減小后還能自動恢復升級到原工作機制。

我們在開發紅包系統時,考慮到原有IDC機房的解決方案對於彈性擴容和流量帶寬支持不太完美,選擇了使用AWS的公有雲作為服務基礎環境。對於第三方的服務,缺少實踐經驗的把握,於是從開發到運維過程中,我們都保持了一種防禦式的思考方式,包括資料庫、緩存節點故障,以及應用服務環境的崩潰、網路抖動,我們都認為隨時可能出問題,都需要對應的自動替換降級策略,嚴重時甚至可通過手動觸發配置開關修改策略。當然,如果組件自身具有降級功能,可以給上層業務節約很多成本資源,要自己實現全部環節的降級能力的確是一件比較耗費資源的事情,這也是一個公司技術慢慢積累的過程。

結束語

以上是我們整個系統研發運維的一些體會。這次春節紅包活動,在資源有限的情況下成功抵抗超乎平常的流量峰值壓力,對於技術而言是一次很大的挑戰,也是一件快樂的事情,讓我們從中積累了很多實踐經驗。未來我們將不斷努力,希望能夠將部分轉化成較為通用的技術,去更好的推動業務成功。真誠希望本文的分享能夠對大家的技術工作有所幫助。

手機QQ春節紅包項目組的復盤總結

推薦一個大型網站技術架構設計相關課程, StuQ 特別邀請 Intel 高級大數據與雲計算專家李智慧老師推出線下小班課程《 大型網站技術架構與案例分析 》,2017年 3月4日—5日,深圳,等你來!具體詳戳「 閱讀原文 」驚喜不停!



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦