一場完美的“秒殺”:API加速的業務邏輯
作者:叢磊,白山雲科技合夥人兼工程副總裁
一天清晨,我被一個客戶電話驚醒,客戶異常焦急,尋問CDN能不能幫助他們解決“秒殺”的問題,他們昨天剛剛進行了“整點秒殺活動”,結果併發量過大,導致服務宕機,使用者投訴。
為了理清思路,我問了對方三個問題:
(1)服務宕機的表現是什麼?
(2)業務的基本架構什麼樣?
(3)秒殺的峰值併發到多少?
順著這些線索,我們先一起還原了應用場景:
該公司是一家P2P理財網站,常有使用者在整點搶購高利率理財產品的“整點秒殺活動”。如上圖所示,終端使用者請求先通過前端負載均衡,然後到達執行實際電商邏輯的Web Server;再下層是執行在VM上的8臺Redis,負責儲存與業務相關的Cache資料,如使用者Profile、理財產品資訊、使用者賬單資訊等。實際落地資料儲存在MySQL中,該MySQL只進行了簡單的分庫分表及讀寫分離。
進行“秒殺”時,先由風控和運營人員選好理財產品,然後標記到資料庫中;活動開始由產品人員放開,終端使用者搶購。
該公司的業務主要來自移動端,平時流量較少,但“秒殺”活動時會瞬間產生大量流量,峰值併發達到10萬以上(其中可能包括bot),如此大的併發主要是集中在以下兩類介面:
對於理財產品的重新整理介面,類似GET /get_fprod.php?uid={$1}&pid={$2}&sid={$3},此類介面的請求量最多,佔比90%。
對於理財產品的下單介面,類似 GET /order_fprod?uid={$1}&pid={$2}&oid={$3}&sid={$4},此類介面的請求量較少,佔比不到1%,但存在大量504超時。
其中uid是使用者ID,pid是理財產品ID,oid是訂單號,sid是隨著客戶端使用者變化的隨機token標識。
場景解讀
根據與客戶溝通得到的場景,初步得到了以下結論:
(1)客戶以移動業務為主,產品通過API在客戶端渲染UI,產品中幾乎沒有靜態資源,頻寬流量不高,傳統CDN無法達到解除安裝壓力的作用;
(2)秒殺時,產生大量502/504超時請求,說明此時使用者請求已超過服務端的業務承載能力,急需擴容。
基於以上兩點,我沒有建議該公司採購CDN服務,而是推薦服務擴容,但隨著我方對於業務更深層次的分析,逐漸發現了一些詭異的事情。
“詭異”現象
(1) 資料庫主從負載極不均衡,通過MySQL管理工具,發現主庫的Query量高達80%;
(2)Redis Cache節點負載極不均衡,通過檢視redis info發現,秒殺時,其中一臺Redis請求量極大,佔比達90%以上,其他Redis請求量卻很低。
上述反常現象激起了雙方技術人員的興趣,這也許就是問題的關鍵!隨著分析深入,第一個現象的原因浮出水面:該公司在使用資料庫時,並未如某些大型電商平臺一樣使用資料庫中介軟體層進行MySQL請求的路由分發,而是在業務程式碼端,使用語言層面的框架完成讀寫分離工作。這帶來了兩個弊端:
- 程式設計師繞過語言層框架開發,並未真正實施讀寫分離;
- 產品人員要求展現效果實時,倒逼開發人員修改業務邏輯,會犧牲讀寫分離,使資料都在主庫讀寫。
接著,第二個現象的原因也逐漸清晰:秒殺時,大量使用者訪問極少數理財產品,當這幾個產品的pid恰好被hash到同一個Redis上,就會導致Cache節點熱點失衡,所有請求最終集中在一個Redis,而這個Redis就是業務的瓶頸!
對症下藥
1. 使用資料庫中介軟體進行讀寫分離以及橫向擴充套件控制
使用資料庫中介軟體可以帶來諸多好處,其中最重要的是可對業務層隱藏部分資料庫細節,更好地控制業務。當然,引入資料庫中間層也存在明顯缺點,在業務整體架構中增加一層元件,違反了“簡單有效”的設計原則。對於很多網際網路公司,在早期甚至中期沒有資料庫中間層也很正常。 但當業務發展到一定階段,引入資料庫中間層是利大於弊的。
基於經驗,我方推薦客戶使用MySQL Route,基本可以滿足簡單需求,如:連線複用;負載均衡;讀寫分離。
上圖是MySQL Router官方架構圖,可以看到,MySQL Router優勢在於外掛化設計,官方提供了一系列外掛供使用。
除MySQL Router,國內還有很多開源資料庫中介軟體可以採用,如阿里、美團等。
使用資料庫中間層,不僅可以解決效能問題,還能在安全方面起到作用,如審計、流量限制等,甚至攔截SQL注入、劣質SQL語句等。
2. 使用API加速服務緩解服務端壓力
Cache服務失衡是比較棘手的問題。“秒殺”時,使用者高頻訪問少數幾個理財產品資訊,當其Cache資料恰巧分配在同一節點,大量請求會瞬間集中到一臺或少數幾臺節點,這就是Cache服務失衡的本質原因。不僅在電商“秒殺”場景中,其他有瞬間熱點訪問的業務型別也會存在這個問題。以微博為例,曾因明星熱點事件導致介面緩慢甚至服務宕機,歸根到底也是這個原因。“爆料”的瞬間,一個微博會在短時間內海量傳播,該微博ID被同時開啟,所有流量會集中到一個Redis節點。
這個問題如何解決?首先,Cache通常以某個資料結構的key為維度進行hash儲存,大量使用者只訪問一個或幾個key時,將導致Redis Cache節點負載不均衡,這是否一定對服務產生影響,則視併發情況而定,但這是一個巨大隱患。針對這個問題,客戶提出了一種解決方案:把一個理財產品的Cache資料再拆散,1個key變成多個,降低key被分配到同一Cache節點的概率。但這種方法存在很大弊端:
(1)需要修改程式碼,原本一條get請求就可以完成的邏輯,要換成多條才能拼成;
(2)日常所有get/set操作的時間消耗都將成倍增加,因為1%的熱點事件增加99%常規操作的時間,嚴重違背二八法則。
基於以上問題,我們推薦客戶使用白山雲聚合CLN-X的“API加速”來解決這個問題。
API加速
API加速完全不同於傳統CDN的鏈路加速,通過快取API返回內容並結合TCP廣域網優化技術,對API請求進行優化。白山API加速將每個API的response資料毫秒級快取在全網邊緣節點,節點記憶體中的response資料以LRU(Least Recently Used)演算法交換。在“熱點事件”時,最熱的資訊持續儲存在邊緣節點,當客戶端訪問該API時,邊緣節點可直接返回結果,不必返回源站。整個架構如下:
API加速服務在網路邊緣節點提供對API的加速能力,包括:API返回結果快取能力、API請求回源網路加速能力。
傳統觀點認為,動態資源(API)無法快取,但白山提出“任何資源都可以被快取,只是過期時間不同”。對於常見的靜態資源,快取過期時間較長;而API並非不能被快取,只是過期時間很短。如一個查詢股價的API,可設定過期時間為50毫秒;百米運動員起跑反應時間為100-200毫秒,50毫秒對於PC端或移動端的使用者體驗並不會造成影響。
沒有快取時,1秒內如有10000個使用者同時訪問,後端承受10000個併發;如果設定50毫秒的快取時間,理論上可將後端併發降低到20個(1秒/50毫秒=20),後端負載降低至五百分之一,其他請求由快取伺服器直接返回給使用者。
綜上所述,白山API加速為客戶提供毫秒級快取,在不影響使用者體驗的前提下提高終端使用者響應速度,同時降低服務端的業務負載壓力。
API加速還支援自定義快取規則,使其更貼近業務,包括QueryString、Header、Path三種類型,針對場景,設定如下規則:
GET /get_fprod.php?uid={$1}&pid={$2}&sid={$3},每個理財產品都有獨立ID,產品資訊不隨使用者ID和客戶端隨機資訊變化,因此Cache key可忽略URI中引數的{$1}和{$3},/get_fprod.php?pid={$2}就是在邊緣節點儲存毫秒級的Cache key。
快取的過期時間如何確定呢?與業務相關,這需要對客戶提供的脫敏日誌進行分析,可初步設定過期時間為500毫秒,最後還需考慮RTT修正值,以適應廣域網環境;RTT則由API加速服務自動捕捉並實時更新。
實際效果
通過為客戶主要的瓶頸介面配置API加速服務,並在峰值時間,從以下兩個維度對比API加速服務開啟與關閉時的效果:
- 終端使用者請求平均響應時間和響應碼200比例
- 服務叢集平均負載
最終效果如下:
如圖A所示,峰值期間終端使用者請求平均響應時間,從3秒左右壓縮至40毫秒以內;如圖B所示,峰值期間所有請求響應碼200的比例從70%左右提升至100%;圖C表示,峰值期間,後端CPU Idle從10%左右提高至97%左右。實測對比資料表明,API加速對降低平均響應時間、提升使用者體驗效果十分顯著,在降低後端伺服器負載方面效果更加明顯,使用API加速的後端CPU Idle可保持在91%以上。
後續建議
資料庫失衡和快取Redis失衡問題已經解決,但除上述問題,還有很多環節可以改善:
1. 佇列服務非同步化請求
目前客戶最終落地資料庫請求直接請求到MySQL,未經佇列緩衝,建議使用佇列服務排隊處理峰值請求,其好處在於能在大訪問量時對請求進行排程,並可控制實際到達資料庫的併發,從而有效保護資料庫後端。
2. API防火牆遮蔽惡意Bot
使用者日誌中含有大量明顯且規律的掃描軟體痕跡,如sqlmap、fimap等,雖然尚未對業務造成較大影響,但卻使服務端資源被佔用。建議在負載均衡最前端對掃描行為予以遮蔽,以提高安全性,同時提升服務效率。除惡意Bot,搶單、刷單等行為也會對服務產生影響,建議使用API防護服務識別與攔截。
3. 產品層考慮服務降級設計
該客戶在整體業務上,沒有服務降級設計,產品功能優先順序未做劃分,導致重要的資料庫、Cache等眾多基礎服務混雜。一旦“秒殺”導致資料庫穿透等嚴重問題時,整體服務將不可用。這種情況應重新梳理業務單元,按照優先順序切分基礎服務,首屏、產品列表、購買、訂單等資訊優先順序最高;其次是非重要功能,如評論、賬單等;如果後端負載較大,必要時可直接捨棄次要功能,從而降低後端負載,保證服務穩定。
總結
解決類似“整點秒殺活動”的情景,是一個系統複雜的工程,就文中客戶暴露出來的資料庫負載不均勻、Cache快取負載不均勻等問題,可通過採用資料庫中間層和API加速等技術解決,最終可取得理想效果。
上述“秒殺”案例,只是API加速的一個典型應用場景,接下來我還會撰文對API加速問題進行更為系統的剖析。