完整秒殺架構的設計

作者:浩宇天尚
鏈接:https://www.jianshu.com/p/dad900926de6

秒殺系統 - 情報背景

相信大家都接觸過新浪微博、淘寶、京東等等這些訪問量較爲巨大的平臺以及網站,針對於 “高流量”、“高併發” 來講,更是我們【技術開發者】都要面臨的的一個很難的 “包袱” 難題。哎,看來如果要在這行混下去,即使你可能沒有接觸高併發場景,也要自己創造 “高併發” 進行迎難而上,因爲只有這樣子我們纔可以更進一步啊!

秒殺系統 - 情報介紹

對於今天我們要介紹的內容就屬於高併發的一個最極端的場景之一:“秒殺”,這個名詞一般會在 “大促” 的時候出現,當然也會在某些平臺活動上出現,那麼肯定會有小夥伴會說,秒殺系統要注意哪些問題啊!爲啥會比較難呢,難在哪裏啊!

秒殺系統 - 特點分析

秒殺系統 - 難度分析

它的難度就在於要完成一個 “60-100 分” 的秒殺系統,那麼它必須要要至少兼顧以下這三個方面,纔算合格,這三個 “惡魔” 分別叫 “服務可用性”、“數據一致性” 和“快速響應性”,有點“苛刻”!

在我們現在的場景下,很難再去考慮一個非分佈式系統的架構了。(分佈式架構)相信大家都知道 CAP 理論吧!沒事不知道也沒關係,可見內容:

CAP 理論又稱 CAP 定理,它說的是在一個分佈式系統中,服務(數據)層面的一致性(Consistency)、服務自身的可用性(Availability)、網絡不同節點分區容錯性(Partition tolerance)。

A 和 C 相信大家從字面上都可以理解了,這裏要聲明一下比較陌生的 P:它代表如果要保證不同的節點即使在網絡出現問題的時候仍能夠訪問到數據,那麼最直接的辦法就是冗餘賦值節點,否則一切都是空談,所以作爲一個分佈式系統而言,無法忽略 P,我們可以理解它就是 A 和 C 的基礎。

CAP 體系總結

以上三者成爲了 “矛盾論”,而 CAP 原則指的是,這三個要素最多隻能同時實現兩點,不可能三者兼顧。

回到我們的主體:秒殺三要素,它們三個可不完全等同於 CAP 三要素,甚至比它們的要求更高,甚至是基於前三者的一個更高層次的水平要求。

服務的可用性(Availability)

服務可用性,是在於高併發流量的衝擊下,仍然可以保持服務的可用性並且還要保證一直可以輸出對外界的服務能力,不會造成宕機以及資源損壞,即使在內存和網絡甚至硬件資源有限的情況下,也不會被擊垮 “死亡”。

比如就像你養魚,你玩命的給魚放飼料,而超過了魚能夠承受的量,它受不了了活活被噎死或者撐死了,這魚就像你的系統一樣,一定要保證魚的健康啊!

數據的一致性(Consistency)

都知道,我們開發的程序以及現在多數的服務器,比如數據庫,他們在處理數據的時候,很有可能會存在多個線程同時在修改同一行數據或者同一塊內存,在 Java 角度而言本身也會存在不一致的問題,而在程序和中間件的角度而言,也是一樣,會出現同一時刻在數據修改順序的亂序化,以及數據的紊亂,造成數據的重複操作,造成與我們預期的設想不同。

服務快速響應性(Quick Response)

一般來講這個屬於用戶體驗,一個較爲合格的秒殺系統,是不應該讓用戶漫長的等待而是最好儘可能快速反饋結果。

總結一下
  1. (異步返回 + 同步處理)總結一下就是異步中套用者同步進行計算,既可以保證快速響應,又可以保證數據的一致性。

  2. (異步返回 + 樂觀鎖處理)總結一下就是異步中套用者樂觀鎖進行計算,既可以保證快速響應,又可以保證數據的一致性。

情報分析結束後,我們要重頭戲!進行技術分析了。

秒殺系統 - 架構設計

我們將秒殺架構進行一下劃分,大體分爲三個層級進行分析:由外到內進行分析,分別是:應用層、服務層、數據訪問層。

秒殺架構設計點

應用層架構設計

動靜分離 + CDN 技術
# 動靜分離分析

# CDN 技術分析
# 突然增加的網絡及服務器帶寬

網站的靜態頁面數據大小 100K,那麼需要的網絡和服務器帶寬是 2G(100K×10000),這些網絡帶寬是因爲秒殺活動新增的,超過網站平時使用的帶寬。

即使將動態業務轉換爲靜態化頁面,但是秒殺活動會非常劇烈的增加的網絡帶寬的消耗,同時並不會減輕前端網站服務器的壓力,所以如果可以的話,需要再進一步將秒殺商品頁面緩存在 CDN,而不在是單純的我們的前端 Nginx 服務器層面,所以需要和 CDN 服務商臨時租借新增的出口帶寬

# 防止緩存干擾頁面刷新爲秒殺頁面
# 通過 javascript 文件進行傳遞隨機號 + 狀態位!

在秒殺商品靜態頁面中加入一個 JavaScript 文件引用,該 JavaScript 文件中包含秒殺開始標誌爲否;

總結一下:前端秒殺頁面使用專門的頁面,這些頁面包括靜態的 HTML 和動態的 JS,他們都需要在 CDN 上緩存。

根據 UID 限制頻率熱度

爲了控制公平性原則,由於黃牛或者一些黑客達人,會採用” 高科技 “,比如說,採用爬蟲腳本操作,瘋狂的去刷新頁面。爲了防止一些人的破壞以及公平分散,所以採用同一個標準去控制 UID(用戶 ID)去訪問頻率信息,當超過每個人所需要達到的頻率閾值,就要進行限制互動窗口內能夠訪問刷新的數據量!

例如:可以用 Redis 給每個用戶做訪問統計,根據用戶的 ID 和商品的標識雙方面進行對用戶對某一個商品的訪問頻率控制,超過訪問頻率後,就會將他的請求暫時性熔斷。

反向代理 + 負載均衡

負載均衡 (Load Balance) 是集羣技術 (Cluster) 的一種應用,可以將工作任務分攤到多個處理單元,從而提高併發處理能力,有利於提升中大型網站的性能。
需要使用服務集羣和水平擴展,讓 “高峯” 請求分流到不同的服務器進行處理。

# http 重定向協議實現負載均衡

根據用戶的 http 請求的 DNAT 計算出一個真實的 web 服務器地址,並將該 web 服務器地址寫入 http 重定向響應中返回給瀏覽器,由瀏覽器重新進行訪問。該方式比較簡單,但性能較差。

一般來講經常用的 SpringCloud-Gateway 或者 Neflix 的 Zuul 等就屬於該類型。

# 協議層:DNS 域名解析負載均衡

DNS 服務器上配置多個域名對應 IP 的記錄。該方式直接將負載均衡的工作交給了 DNS,爲網站管理維護省掉了很多麻煩,訪問速度快,有效改善性能。

一般來講經常用的 DNS 服務器或者國內的 DNS 服務器等就屬於該類型。

# 協議層:反向代理負載均衡

反向代理服務器在提供負載均衡功能的同時,管理着一組 web 服務器,根據負載均衡算法將請求的瀏覽器訪問轉發到不同的 web 服務器處理,處理結果經過反向服務器返回給瀏覽器。

該方式部署簡單,web 服務器地址不能直接暴露在外,不需要使用外部 IP 地址,而反向代理服務作爲溝通橋樑就需要配置雙網卡、外部內部兩套 IP 地址。

一般來講經常用的 Nginx 或者 HaProxy 等就屬於該類型。

# 網絡層 IP 負載均衡

網絡層通過修改目標地址進行負載均衡,該方式在響應請求時速度較反向服務器負載均衡要快,但是,當請求數據較大 (大型視頻或文件) 時,速度反應就會變慢。

一般來講經常用的 Nginx 或者 HaProxy 等就屬於該類型。

# 數據鏈路層負載均衡

數據鏈路層修改 Mac 地址進行負載均衡,負載均衡服務器的 IP 和它所管理的 web 服務羣的虛擬 IP 一致。它不需要負載均衡服務器進行地址的轉換,但是對負載均衡服務器的網卡帶寬要求較高。

一般來講經常用的 LVS 等就屬於該類型。

# F5 和 A10 負載均衡器

F5 的全稱是 F5-BIG-IP-GTM,硬件負載均衡設備,其併發能力達到。該方式能夠實現多鏈路的負載均衡和冗餘,可以接入多條 ISP 鏈路,在鏈路之間實現負載均衡和高可用。

服務層架構設計

緩存技術分析

硬盤持久化的 IO 操作將耗費大量資源。所以決定採用基於內存操作的 redis,redis 的密集型 io

分批放行 + 排隊處理
# 排隊處理

排隊處理機制,正如,我們日常買東西排隊一樣的道理,這樣子就不會處理不過來,並且也可以保證數據執行的正確性!

它直接將請求放入隊列中的,採用 FIFO(First Input First Output,先進先出),這樣的話,我們就不會導致某些請求永遠獲取不到鎖。看到這裏,有一些將多線程處理方式變成單線程處理機制,會大大影響數據的效率和性能!

# Java 的三個常用的併發隊列

在請求預處理階段,由於系統入隊需求要遠大於出隊需求,一般不會出現隊空的情況,所以我們可以選擇 ConcurrentLinkedQueue 來作爲我們的請求隊列實現,甚至可以採用 Disruptor 異步處理框架機制。

# 分批放行

在同步排隊的基礎上,我們可以在加入一個分批放行執行處理機制。

顧名思義的就是,爲了提高性能,我們可以考慮達到預定閾值以後,在進行相關的執行後端服務,這樣子可以提高一定的性能以及減少後端請求的次數和壓力,如下圖所示:

還會利用緩存和隊列技術減輕應用處理的壓力,通過異步請求的方式做到最終一致性。

限流
漏桶算法

漏桶算法思路很簡單,水(請求)先進入到漏桶裏,漏桶以一定的速度出水,當水流入速度過大會直接溢出,可以看出漏桶算法能強行限制數據的傳輸速率。

# 算法弊端

速度執行敏感度不高!無論輸入速率如何波動,均不會體現在服務端,即使資源有空餘,對於突發請求也無法及時處理,故對有突發請求處理需求時,不宜選擇該方法。

令牌桶算法

令牌桶算法的原理是系統會以一個恆定的速度往桶裏放入令牌,而如果請求需要被處理,則需要先從桶裏獲取一個令牌,當桶裏沒有令牌可取時,則拒絕服務。

# 實現原理

設定令牌桶中添加令牌的速率,並且設置桶中最大可存儲的令牌,當請求到達時,向桶中請求令牌(根據應用需求,可能爲 1 個或多個),若令牌數量滿足要求,則刪除對應數量的令牌並通過當前請求,若桶中令牌數不足則觸發限流規則。

爲解決固定窗口計數帶來的週期切換處流量突發問題,可以使用滑動窗口計數。滑動窗口計算本質上也是固定窗口計數,區別在於將計數週期進行細化

滑動窗口

滑動窗口計數法與固定窗口計數法相比較,除了計數週期 T 及週期內最大訪問(調用)數 N 兩個參數,增加一個參數 M,用於設置週期 T 內的滑動窗口數。

數據訪問層

由於要承受高併發,mysql 在高併發情況下的性能下降尤其嚴重。

數據更新點(庫存扣除)
悲觀鎖更新數據

可以從 “悲觀鎖” 的方向

行鎖、頁鎖、表鎖、同步鎖、分佈式鎖、分佈式隊列、意向所等。

樂觀鎖更新數據

討論一下 “樂觀鎖” 的思路了。

緩存樂觀鎖、數據庫樂觀鎖。(判斷更新行數是否 > 0),CAS 機制

姊妹篇【「絕密檔案」“爆料” 完整秒殺架構的設計到技術關鍵點的 “八卦資料”】

再此會進行擴展技術介紹,以下內容:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/kfp3JJHddKTBa0JeeWDKgQ