秒殺系統的架構解決之道

duym0065 7年前發布 | 32K 次閱讀 Nginx 數據庫 軟件架構

本文將會從三個方面來分別探討如何設計應用架構以更好的支持“秒殺”類需求,包括秒殺帶來的問題和挑戰、產品架構解決之道、流量控制解決方案。

秒殺場景下帶來的海量用戶所造成的流量突增對系統沖擊力可想而知,瞬時流量之高一方面造成的讀寫沖突,數據庫鎖會非常嚴重,另一方面應用服務器能否撐住也是一個問題。同時由于秒殺業務一般是各種活動帶來,需要快速上下線,這在開發上也會提出更高的標準,快速支持需求而不出錯。從系統上講我們要做到高可用和高并發;從開發效率上我們要做到敏捷開發以支持產品快速迭代。接下來讓我們從產品架構和流量控制這兩個方面來討論并解決這個問題。如果把解決秒殺問題看成一種武林秘籍,則產品架構是內功,流量控制是招法,上乘內力搭配制勝招法則無往而不利。

內功:產品架構解決之道

向一站式架構說"NO"

上圖是一個典型的一站式架構,未經過良好的設計的系統隨著時間的推移逐漸會轉換成上圖這樣,一方面是產品業務的壓力,另一方面也是先天的設計不足。一站式架構存在很多問題。

首先就是不易擴展,難以維護。隨著業務的發展,對系統進行擴容難以避免,而一站式架構的可擴展性卻令人堪憂,對其擴容則相當于對系統中的所有功能進行擴容,從代碼確認到測試再到環境都是一項艱巨的任務。一站式架構中的擴容往往建立在“既然什么都沒改,則運行應該正常”的假設之上,而擴容遷移中的各種BUG則隱藏在這種假設之中。系統所需的各種權限也隨著規模而不斷增多,連接的各種組件也越來越多,由此帶來的維護性問題也會持續加重。

其次是代碼難以理解,開發質量得不到保證。隨著系統規模的擴大,即便是最初經過良好設計的代碼,經過人員迭代,需求壓力,也難免會逐漸走向混亂。漸漸的,各種跨層調用,不合適的封裝,有副作用的函數等都會出現在代碼中,這些設計侵入到代碼中的每個角落,每個人都能見,而每個人都會選擇性忽略,因為錯綜復雜的調用難以調整,也很難找到人員來進行整體測試。而這些都是BUG產生的溫床。

最后在一站式架構下項目的可靠性是無法得到保證的,由于業務調整而修改一處邏輯,往往會影響到代碼中很多功能的邏輯,而這些額外受到影響的功能則通常不會得到測試,只是在假定這些都是正常的,而這種假定也通常正確,直接潛在的問題爆發。該出錯的總會出錯,墨菲定律往往這時候是最有效的。

綜上可以看出一站式架構設計與敏捷開發格格不入,持續開發,持續集成,持續部署也就只能變成空談了。

微服務架構

上圖是優化后的微服務架構,將整個系統拆分成訂單、推送、折扣、產品、個人信息等各個微服務,每個服務都有自己的數據庫和緩存等,并且不會互相交叉。各服務之間使用消息隊列、RPC調用等傳輸數據。目前大部分系統設計可能處于一站式架構和微服務架構之間,即上層應用可能已經服務化,但是數據庫層面還是使用同一個庫。但個人以為系統應該朝完全微服務化努力,隔離數據庫層面的共用,以獲得更高的系統可靠性。

微服務架構下的系統更加容易進行擴展,可以只針對需要擴容的系統來進行擴容,例如訂單量增大,可以只擴容訂單服務,而對于其他服務例如個人信息、折扣中心等都都不進行調整,這樣一方面減少了系統擴容而對整體穩定性帶來的變化,即只需測試新環境中的訂單服務即可,隨著微服務拆分的越細,這種優勢也就越大。

在這種微服務架構下,開發人員可以更加集中精力,將重點放到少量的代碼和明確的業務上,這樣能夠產出更加優雅的代碼和良好的設計,在代碼優化調整中,也不會由于到處調用而畏手畏腳。每個微服務可以安排2-3人的小組專門維護,這樣也會減少一個微服務內部的溝通成本,而進一步提高生產力。每個微服務小組可以獨立工作,無需過多協調即可實踐新功能或想法。

隨著技術的不斷發展,項目所用的技術架構總會過時,在系統技術革新上,對于傳統的一站式架構,甚至是之前提到的服務化架構在應用新技術上都會遇到不小的困難,牽一發動全身,技術改革往往除了推倒重來而沒有其他辦法。對于微服務架構,由于系統的完全拆分,公共組件依賴只剩下異步的消息隊列,在新技術應用這方面則有了天然優勢。微服務基于組件開發設計,提供了在開發過程中技術選型的最大靈活性,甚至是編程語言的變化都可以進行嘗試。

最后要提到的,就是系統穩定性和可用性方面的考慮。在業務需求的持續推動下,持續部署不可避免,在線系統隨時都需要進行上線。隨著業務的增長、系統的復雜,系統部署時造成問題的潛在可能性會大大提高。而微服務在這方面極大的提高了系統的可靠性。由于微服務的劃分,故障天然被隔離,某個服務的故障,不會造成系統的整體癱瘓。而發布的時間由于只需要發布更新相關的服務而大大縮短,這也提高了整體系統的穩定性。當面臨問題需要回滾時,也只需要回滾更新相關服務即可,而這在一站式架構中將會是一個災難。

良好的架構可以更好的支持快速迭代。高內聚的設計將開發人員精力集中到相對集中的領域以設計更優雅的代碼實現。隨著技術的演進,項目架構也可以跟著一起迭代升級。也可以更好的支持持續集成、持續部署。總之,微服務可以滲透到開發中的每個領域為業務迭代提供更好的支持。微服務這方面建議可以參考Spring Cloud和Docker。

招法:流量控制解決方案

內功的修煉固然重要,不過并非一朝一夕可成,是需要長期的努力和不斷的沉淀。在武學中固然有高神內力,同時也存在一些致勝招法,一旦練成即可功力猛進,下來就讓我們看一下支持秒殺業務中的一些致勝招法:流量控制解決方案。

流量控制解決總覽

如上圖所示,在項目的整個架構中,流量要做到逐層減少。在每層中都可以使用一些方法來減少流量。

前端流量控制

前端流量控制,頁面可以設計為動靜分離,將盡可能多的數據使用CDN進行緩存,以減少到自己服務器上的流量。同時可以加入驗證問題,拉長用戶下單時間并防止刷單。對較核心邏輯擔心用戶破譯驗證方式,可以再增加服務器端的用戶ID訪問限制,例如同一用戶5s內只能觸發1次相關操作等。

反向代理流量控制

反向代理(Nginx)流量控制,很多頁面或者接口響應數據都可以進行靜態化處理,應用側(Tomcat)可以定時生成這些資源,發到內容分發服務上,內容分發服務可以將這些靜態化資源分發到所有的反向代理上。這些數據可以根據需要按照一定時間間隔進行更新。同時,也可以利用Nginx中的緩存配置功能,對熱點接口進行緩存。

借助Nginx中Nginx-lua插件的功能,一些簡單邏輯在Nginx中直接實現會比較容易,使用恰當,能夠大幅減少到應用服務器的請求。例如倒計時,取系統當前時間等邏輯就非常適合在Nginx中使用Lua實現。對于存在緩存中的商品數量等信息也可由Nginx直接訪問緩存返回,而不將請求再轉發給應用服務器。

訪問數據分析流量控制

在秒殺中,令人頭疼的問題不只是正常流量突增。由于秒殺活動一般都帶有優惠性質,惡意訪問也會增多,對于惡意請求在Nginx層也可以做很多工作,進行一次攔截。Nginx配置中有限制用戶訪問頻率的配置可以根據需要進行配置。同時,也可以使用nginx-lua完成一些簡單的封禁邏輯,例如調用接口可以封掉指定IP或者UA的訪問請求。之后利用日志分析程序,對訪問日志進行分析,將需要封掉的IP或者UA等信息調用Nginx上提供的接口進行封殺。

應用服務器流量控制

經過之前的處理,能夠到達應用服務器的流量已經少了很多。對于秒殺活動這類的需求,可以準備專門的活動服務器,專門處理相關邏輯。可以使用不同域名進行分流,或者按照一定URL規則在反向代理服務器上進行分流。同時,對于到數據庫進行操作的請求可以使用阻塞隊減少到數據庫的訪問以減少行鎖等。對于明顯超出處理范圍的請求可以直接返回秒殺已經結束。例如商品總量300,剩余量100,每臺應用服務器上待處理的值超過總量的值則可以直接返回秒殺結束。剩余量可以存入緩存服務中,剩余量可以不那么精確,確保緩存中的剩余量>=實際剩余量即可。超出剩余量的請求也可以直接返回秒殺結束。

緩存存儲多點部署以提高系統的整體可用性,避免緩存問題導致系統出錯的情況。Redis會是一個目前比較好的選擇,主從自動切換等功能可以更好的增強系統整體可用性。對于使用單點Memcached等系統,建議可以配置Keepalived,使得發生機器故障時,IP可以自動轉移到另外一臺健康Memcached服務器上,雖然數據不可恢復,但是緩存組件依然可用。宕機的組件對系統造成的影響往往是不可預料的,尤其是在未良好設計的系統中,整體架構中應任一組件完全宕機的可能性。

對于秒殺活動需求,可能是一系列的活動,相關邏輯可以判斷是否可以做到一個專門活動庫中,并進行讀寫分離設計。有條件的情況下,數據庫中間件會完成其中一部分工作。在沒有相關條件的時候,即使沒有數據庫中間件,相關邏輯也可以直接在代碼中實現。

對于數據庫行鎖問題,如有需要可以進一步優化,例如上圖,將行鎖問題通過拆分具體商品,增加分配ID標志,去掉行鎖。這個變動的缺點是可能對現有業務邏輯影響較大。不過優點也很明顯,在數據庫層面消除了行鎖。

流量控制總結

一圖勝千言,以上討論的流量控制總結方案可以總結到一張圖上:

總結

結合微服務架構,我們最終的架構圖可以是這樣的:

以上供大家參考。

技術選型簡介

平臺應用構建技術選型如下,供大家參考:

  • 反向代理層Nginx推薦使用阿里開源的Tengine,Tengine中增強了Nginx中的很多功能并簡化了配置。
  • 在虛擬化方面,可以按照物理機->虛擬機->Docker三層架構來實現虛擬化,在物理機上按照應用實例、緩存實例、消息隊列實例等建立虛擬機,再分別在虛擬機上啟動不同實例的Docker。
  • 在應用構建方面,推薦使用Spring Cloud解決方案(由于歷史原因,目前還保留部分Dubbo接口)。
  • 消息隊列選型目前比較廣泛,這里推薦一下阿里開源的RocketMQ,目前已經捐贈給Apache。我們內部也針對RocketMQ開發了一套易用client,當然也在開源計劃之中,相信不久就會和大家見面。目前我們系統每分鐘接口請求量高峰在幾十萬左右,系統整體支撐再多的訪問量相信也沒有問題,各層、各服務根據需要都可以進行橫向擴容以支撐更高的訪問量。

參考資料

  • http://projects.spring.io/spring-cloud/
  • http://projects.spring.io/spring-boot/
  • http://nginx.org/en/docs/
  • http://tengine.taobao.org/documentation_cn.html
  • http://dubbo.io/User+Guide-zh.htm
  • https://github.com/apache/incubator-rocketmq
  • 崔力強,張駿 譯. Sam Newman 著. 微服務設計
  • 李兆海,劉斌,巨震 譯. 詹姆斯·特恩布爾(James Turnbull) 著. 第一本Docker書 修訂版

 

來自:http://www.infoq.com/cn/articles/solution-to-the-architecture-of-spike-system

 

 本文由用戶 duym0065 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!