《高可用架構第1卷【3】》 ——電商架構熱點專題

 《高可用架構第1卷【2】》  html

 

《高可用架構第1卷【1】》前端

http://www.noobyard.com/article/p-ytshkmyz-by.htmljava

 

 

第3 章 電商架構熱點專題.205
張開濤/3.1 億級商品詳情頁架構演進技術解密.205
3.1.1 商品詳情頁205
3.1.2 商品詳情頁發展史209
3.1.3 遇到的一些問題和解決方案220
3.1.4 總結228
3.1.5 疑問與解惑229node

 


 

億級商品詳情頁架構演進技術解密 | 高可用架構系列 - 簡書此文是開濤在【三體高可用架構羣】之分享內容,「三體」 是爲了記念三體一書對技術人的偉大影響而冠名。 張開濤:2014 年加入京東,主要負責商品詳情頁、詳情頁統一服務架構與開發工做...react

此文是開濤在【三體高可用架構羣】之分享內容,「三體」 是爲了記念三體一書對技術人的偉大影響而冠名。nginx

張開濤:2014 年加入京東,主要負責商品詳情頁、詳情頁統一服務架構與開發工做,設計並開發了多個億級訪問量系統。工做之餘喜歡寫技術博客,有《跟我學 Spring》、《跟我學 Springgit

MVC》、《跟我學 Shiro》、《跟我學 Nginx+Lua 開發》等系列教程,博客程序員

http://jinnianshilongnian.iteye.com/  的訪問量超過 500W。github

京東 618 的硝煙雖已散去,可開發和備戰 618 期間總結過的一些設計原則和遇到的一些坑還歷歷在目。伴隨着網站業務發展,需求日趨複雜多樣並隨時變化;傳統靜態化方案會遇到業務瓶頸,不能知足瞬變的需求。所以,須要一種能高性能實時渲染的動態化模板技術來解決這些問題。web

今夜,咱們將進行服裝品類的垂直詳情頁的 AB 測試和切新庫存服務的 1/n 流量。就此機會,和你們分享一下最近一年作的京東商品詳情頁的架構升級的心路歷程。

商品詳情頁是什麼

商品詳情頁是展現商品詳細信息的一個頁面,承載在網站的大部分流量和訂單的入口。京東商城目前有通用版、全球購、閃購、易車、惠買車、服裝、拼購、今日抄底等許多套詳情頁模板,經過一些特殊屬性、商家類型和打標來區分,每套模板數據是同樣的,核心邏輯基本同樣,可是一些前端邏輯是有差異的。

目前商品詳情頁個性化需求很是多,數據來源也是很是多的(目前統計後端有差很少數十個依賴服務),並且許多基礎服務作不了的不想作的或者說須要緊急處理的都放咱們這處理,好比一些屏蔽商品需求等。所以咱們須要一種架構能快速響應和優雅的解決這些需求問題,來了問題能在 5~10 分鐘內搞定。咱們這邊經還常收到一些緊急需求,好比工商的一些投訴等須要及時響應。以前架構是靜態化的,確定沒法知足這種日趨複雜和未知的需求。靜態化時作屏蔽都是經過 js,因此咱們從新設計了商品詳情頁的架構。

它主要包括如下三部分:

商品詳情頁系統

負責靜的部分(整個頁面)

商品詳情頁動態服務系統和商品詳情頁統一服務系統

統一服務系統負責動的部分,好比實時庫存。目前已經上線了幾個核心服務,今晚計劃切新庫存服務的 1/n 流量。

動態服務系統負責給內網其餘系統提供一些數據服務(好比大客戶系統須要商品數據),目前商品詳情頁系統已經穩定運行半年了,目前主要給列表頁提供一些數據。

鍵值結構的異構數據集羣

商品主數據由於是存儲在 DB 中,對於一些聚合數據須要聯合查詢很是多,會致使查詢性能差的問題,所以對於鍵值類型的查詢,咱們這套異構數據很是有用。咱們此次架構的調整的主要目的是知足日趨複雜的業務需求,能及時開發業務方的需求。咱們的系統主要處理鍵值數據的邏輯,關係查詢咱們有另外一套異構系統。

下圖是咱們的模板頁,核心數據都是同樣的,只是展現方式和一些前端邏輯不太同樣。

 

 

 

 

 

 

咱們詳情頁的前端展現主要分爲這麼幾個維度:

  • 商品維度 (標題、圖片、屬性等)
  • 主商品維度(商品介紹、規格參數)
  • 分類維度
  • 商家維度
  • 店鋪維度

另外還有一些實時性要求比較高的如實時價格、實時促銷、廣告詞、配送至、預售等是經過異步加載。

咱們目前把數據按維度化存儲,好比一些維度直接 redis 存,性能好

 

 

京東商城還有一些特殊維度數據:好比套裝、手機合約機等,這些數據是主商品數據外掛的,經過異步加載來實現的邏輯。還有一些與第三方合做的,如易車,不少數據都是沒法異構的,都是直接異步加載的。目前有易車、途牛等一些公司有這種合做。

咱們 618 當天 PV 數億,服務器端 TOP99 響應時間低於 38ms(此處是第 1000 次中第 99 次排名的時間,PV 具體數據不便公開,但 TOP99 基本在 40ms 以內)。

 

 

上圖是咱們的一個監控圖。咱們詳情頁流量特色是離散數據,熱點少,各類爬蟲、比價軟件抓取;因此若是直接查庫,防刷沒作好,很容易被刷掛

商品詳情頁發展史

 

 

這是咱們的一個架構歷史。

架構 1.0

 

 

IIS+C#+Sql Server,最原始的架構,直接調用商品庫獲取相應的數據,扛不住時加了一層 memcached 來緩存數據。

這種方式常常受到依賴的服務不穩定而致使的性能抖動。基本發展初期都是這個樣子的,扛不住加層緩存。所以咱們設計了架構 2.0。

架構 2.0

 

 

該方案使用了靜態化技術,按照商品維度生成靜態化 HTML,這就是一個靜態化方案。

主要思路:

經過 MQ 獲得變動通知;

經過 Java Worker 調用多個依賴系統生成詳情頁 HTML;

經過 rsync 同步到其餘機器;

經過 Nginx 直接輸出靜態頁;

接入層負責負載均衡。

主要缺點:

假設只有分類、麪包屑變動了,那麼全部相關的商品都要重刷;

隨着商品數量的增長,rsync 會成爲瓶頸;

沒法迅速響應一些頁面需求變動,大部分都是經過 JavaScript 動態改頁面元素。

以前需求沒那麼多,所以頁面變動不是很頻繁,基本沒什麼問題。可是隨着商品數量的增長這種架構的存儲容量到達了瓶頸,並且按照商品維度生成整個頁面會存在如分類維度變動就要所有刷一遍這個分類下全部信息的問題,所以咱們又改造了一版按照尾號路由到多臺機器。這種生成整個頁面的方案會存在好比只有分類信息變了,也須要把這個分類下的商品從新刷一遍。

架構 2.1

 

 

主要思路:

容量問題經過按照商品尾號作路由分散到多臺機器,按照自營商品單獨一臺,第三方商品按照尾號分散到 11 臺;

按維度生成 HTML 片斷(框架、商品介紹、規格參數、麪包屑、相關分類、店鋪信息),而不是一個大 HTML;

經過 Nginx SSI 合併片斷輸出;——SSI 是一種相似於 ASP 的基於服務器的網頁製做技術。將內容發送到瀏覽器以前,能夠使用「服務器端包含 (SSI)」指令將文本、圖形或應用程序信息包含到網頁中。

接入層負責負載均衡;

多機房部署也沒法經過 rsync 同步,而是使用部署多套相同的架構來實現。

這種方式經過尾號路由的方式分散到多臺機器擴容,而後生成 HTML 片斷,按需靜態化;當時咱們作閃購的時候,須要加頁頭,都是經過 js 搞定的。但對於大的頁面結構變動,須要全量生成。尤爲像麪包屑不同的話會很麻煩,須要生成多個版本

主要缺點:

碎片文件太多,致使如沒法 rsync;

機械盤作 SSI 合併時,高併發時性能差,此時咱們尚未嘗試使用 SSD;

模板若是要變動,數億商品須要數天才能刷完;

到達容量瓶頸時,咱們會刪除一部分靜態化商品,而後經過動態渲染輸出,動態渲染系統在高峯時會致使依賴系統壓力大,抗不住;

仍是沒法迅速響應一些業務需求。

當時我記得印象最深的就是碎片文件太多,咱們的 inode 不夠了,常常要半夜去公司刪文件。由於存在刪除問題,每臺服務器並非全量,因此咱們須要一個動態生成的服務,當靜態化不存在的時候還原到動態服務;但這樣雙十一時壓力很是大,咱們依賴的系統隨時都給咱們降級。

架構 3.0

咱們的痛點:

以前架構的問題存在容量問題,很快就會出現沒法全量靜態化,仍是須要動態渲染;(對於全量靜態化能夠經過分佈式文件系統解決該問題,這種方案沒有嘗試)

最主要的問題是隨着業務的發展,沒法知足迅速變化、還有一些變態的需求。

其實最痛快的是業務來講咱們要搞垂直,咱們要模塊化,咱們要個性化;這些通通很差搞,所以咱們就考慮作一版全動態的。其實思路和靜態化差很少, 數據靜態化聚合、頁面模板化。

咱們要考慮和要解決的問題:

能迅速響瞬變的需求,各類變態需求;

支持各類垂直化頁面改版;

頁面模塊化;

AB 測試;

高性能、水平擴容;

多機房多活、異地多活。

 

 

這是咱們新的系統:三個子系統。

主要思路:

數據變動仍是經過 MQ 通知;

數據異構 Worker 獲得通知,而後按照一些維度進行數據存儲,存儲到數據異構 JIMDB 集羣(JIMDB:Redis + 持久化引擎,是基於 Redis 改造的一個加了持久化引擎的 KV 存儲),存儲的數據都是未加工的原子化數據,如商品基本信息、商品擴展屬性、商品其餘一些相關信息、商品規格參數、分類、商家信息等;

數據異構 Worker 存儲成功後,會發送一個 MQ 給數據同步

Worker,數據同步 Worker 也能夠叫作數據聚合 Worker,按照相應的維度聚合數據存儲到相應的 JIMDB 集羣;三個維度:基本信息(基本信息 + 擴展屬性等的一個聚合)、商品介紹(PC 版、移動版)、其餘信息(分類、商家等維度,數據量小,直接 Redis 存儲);

前端展現分爲兩個:商品詳情頁和商品介紹,使用 Nginx+Lua 技術獲取數據並渲染模板輸出。

思路差很少: MQ 獲得變動通知,Worker 刷元數據到 JIMDB,前端展現系統取數據渲染模板。另外咱們當時架構的目標是詳情頁上有的數據,咱們均可以提供服務出去,主要提供單個商品的查詢服務,因此咱們把這個系統叫作動態服務系統。

 

 

該動態服務分爲前端和後端,即公網仍是內網,如目前該動態服務爲列表頁、商品對比、微信單品頁、總代等提供相應的數據來知足和支持其業務。

目前天天爲列表頁提供增量數據服務。微信上京東入口看到的詳情頁 也是咱們這個服務提供的數據。APP 的數據暫時沒走咱們的系統,不過咱們目前系統實現的是日常流量的 50 倍左右,性能和流量基本不是問題。咱們詳情頁架構設計的一些原則:

數據閉環

數據維度化

拆分系統

Worker 無狀態化 + 任務化

異步化 + 併發化

多級緩存化

動態化

彈性化

降級開關

多機房多活

多種壓測方案

由於咱們這邊主要是讀服務,所以咱們架構可能偏讀爲主的設計;目前我設計的幾個系統都遵循這些原則去設計:

數據閉環:

 

 

數據閉環,即數據的自我管理,或者說是數據都在本身系統裏維護,不依賴於任何其餘系統,去依賴化,這樣獲得的好處就是別人抖動跟我不要緊。所以咱們要先數據異構。

數據異構,是數據閉環的第一步,將各個依賴系統的數據拿過來,按照本身的要求存儲起來;咱們把不少數據劃分爲三個主要維度進行異構:商品信息、商品介紹和其餘信息(分類、商家、店鋪等)。

數據原子化處理,數據異構的數據是原子化數據,這樣將來咱們能夠對這些數據再加工再處理而響應變化的需求。咱們有了一份原子化異構數據雖然方便處理新需求,但偏偏由於第一份數據是原子化的,那麼它會很分散,前端讀取時 mget 的話 性能不是很好,所以咱們又作了數據聚合。

數據聚合,是將多個原子數據聚合爲一個大 JSON 數據,這樣前端展現只須要一次 get,固然要考慮系統架構,好比咱們使用的 Redis 改造,Redis 又是單線程系統,咱們須要部署更多的 Redis 來支持更高的併發另外存儲的值要儘量的小

數據存儲,咱們使用 JIMDB,Redis 加持久化存儲引擎,能夠存儲超過內存 N 倍的數據量,咱們目前一些系統是 Redis+LMDB 引擎的存儲,目前是配合 SSD 進行存儲;另外咱們使用 Hash Tag 機制把相關的數據哈希到同一個分片,這樣 mget 時不須要跨分片合併分片邏輯使用的是 Twemproxy,和應用端混合部署在一塊兒;減小了一層中間層,也節約一部分機器

咱們目前的異構數據是鍵值結構的,用於按照商品維度查詢,還有一套異構時關係結構的用於關係查詢使用。

數據維度化

對於數據應該按照維度和做用進行維度化,這樣能夠分離存儲,進行更有效的存儲和使用。咱們數據的維度比較簡單:

商品基本信息,標題、擴展屬性、特殊屬性、圖片、顏色尺碼、規格參數等;

這些信息都是商品維度的。

商品介紹信息,商品維度商家模板、商品介紹等;

京東的商品比較特殊:自營和第三方。

自營的商品能夠任意組合,選擇其中一個做爲主商品,所以他的商品介紹是商品維度。

第三方的組合是固定的,有一個固定的主商品,商品介紹是主商品維度。

非商品維度其餘信息,分類信息、商家信息、店鋪信息、店鋪頭、品牌信息等;

這些數據量不是很大,一個 redis 實例就能存儲。

商品維度其餘信息(異步加載),價格、促銷、配送至、廣告詞、推薦配件、最佳組合等。

這些數據不少部門在維護,只能異步加載;目前這些服務比較穩定,性能也不錯,咱們在把這些服務在服務端聚合,而後一次性吐出去。如今已經這麼作了幾個,好比下面這個就是在服務端聚合吐出去的狀況。

 

 

http://c.3.cn/recommend?callback=jQuery4132621&methods=accessories%2Csuit&p=103003&sku=1217499&cat=9987%2C653%2C655&lid=1&uuid=1156941855&pin=zhangkaitao1987&ck=pin%2CipLocation%2Catw%2Caview&lim=6&cuuid=1156941855&csid=122270672.4.1156941855%7C91.1440679162&c1=9987&c2=653&c3=655&_=1440679196326

這是咱們 url 的一些規則,methods 指定聚合的服務。咱們還對系統按照其做用作了拆分。

拆分系統

 

 

將系統拆分爲多個子系統雖然增長了複雜性,可是能夠獲得更多的好處。好比,數據異構系統存儲的數據是原子化數據,這樣能夠按照一些維度對外提供服務;而數據同步系統存儲的是聚合數據,能夠爲前端展現提供高性能的讀取。而前端展現系統分離爲商品詳情頁和商品介紹,能夠減小相互影響;目前商品介紹系統還提供其餘的一些服務,好比全站異步頁腳服務。咱們後端仍是一個任務系統。

Worker 無狀態化 + 任務化

 

 

數據異構和數據同步 Worker 無狀態化設計,這樣能夠水平擴展;

應用雖然是無狀態化的,可是配置文件仍是有狀態的,每一個機房一套配置,這樣每一個機房只讀取當前機房數據;

任務多隊列化,等待隊列、排重隊列、本地執行隊列、失敗隊列;

隊列優先級化,分爲:普通隊列、刷數據隊列、高優先級隊列;

例如,一些秒殺商品會走高優先級隊列保證快速執行。

副本隊列,當上線後業務出現問題時,修正邏輯能夠回放,從而修復數據;能夠按照好比固定大小隊列或者小時隊列設計

在設計消息時,按照維度更新,好比商品信息變動和商品上下架分離,減小每次變動接口的調用量,經過聚合 Worker 去作聚合。

異步化 + 併發化

咱們系統大量使用異步化,經過異步化機制提高併發能力。首先咱們使用了消息異步化進行系統解耦合,經過消息通知我變動,而後我再調用相應接口獲取相關數據;以前老系統使用同步推送機制,這種方式系統是緊耦合的,出問題須要聯繫各個負責人從新推送還要考慮失敗重試機制。數據更新異步化,更新緩存時,同步調用服務,而後異步更新緩存。

可並行任務併發化,商品數據系統來源有多處,可是能夠併發調用聚合,這樣原本串行須要 1s 的通過這種方式咱們提高到 300ms 以內。異步請求合併,異步請求作合併,而後一次請求調用就能拿到全部數據。前端服務異步化 / 聚合,實時價格、實時庫存異步化,使用如線程或協程機制將多個可併發的服務聚合。異步化還一個好處就是能夠對異步請求作合併,原來 N 次調用能夠合併爲一次,還能夠作請求的排重。

多級緩存化

因以前的消息粒度較粗,咱們目前在按照一些維度拆分消息,所以讀服務確定須要大量緩存設計, 因此咱們是一個多級緩存的系統。

瀏覽器緩存,當頁面之間來回跳轉時走 local cache,或者打開頁面時拿着 Last-Modified 去 CDN 驗證是否過時,減小來回傳輸的數據量;

CDN 緩存,用戶去離本身最近的 CDN 節點拿數據,而不是都回源到北京機房獲取數據,提高訪問性能;

服務端應用本地緩存,咱們使用 Nginx+Lua 架構,使用 HttpLuaModule 模塊的 shared dict 作本地緩存( reload 不丟失)或內存級 Proxy Cache,從而減小帶寬

咱們的應用就是經過 Nginx+Lua 寫的,每次重啓共享緩存不丟,這點咱們受益頗多,重啓沒有抖動,另外咱們還使用使用一致性哈希(如商品編號 / 分類)作負載均衡內部對 URL 重寫提高命中率;咱們對 mget 作了優化,如去商品其餘維度數據,分類、麪包屑、商家等差很少 8 個維度數據,若是每次 mget 獲取性能差並且數據量很大,30KB 以上;而這些數據緩存半小時也是沒有問題的,所以咱們設計爲先讀 local

cache,而後把不命中的再回源到 remote cache 獲取,這個優化減小了一半以上的 remote

cache 流量;這個優化減小了這個數據獲取的一半流量;

服務端分佈式緩存,咱們使用內存 + SSD+JIMDB 持久化存儲。

動態化

咱們整個頁面是動態化渲染,輸出的數據獲取動態化,商品詳情頁:按維度獲取數據,商品基本數據、其餘數據(分類、商家信息等);並且能夠根據數據屬性,按需作邏輯,好比虛擬商品須要本身定製的詳情頁,那麼咱們就能夠跳轉走,好比全球購的須要走 jd.hk 域名,那麼也是沒有問題的;將來好比醫藥的也要走單獨域名。

模板渲染實時化,支持隨時變動模板需求;咱們目前模板變動很是頻繁,需求很是多,一個頁面 8 個開發。

重啓應用秒級化,使用 Nginx+Lua 架構,重啓速度快,重啓不丟共享字典緩存數據;其實咱們有一些是 Tomcat 應用,咱們也在考慮使用如 Tomcat+Local Redis 或 Tomcat+Nginx Local Shared Dict 作一些本地緩存,防止重啓堆緩存失效的問題。

需求上線速度化,由於咱們使用了 Nginx+Lua 架構,能夠快速上線和重啓應用,不會產生抖動;另外 Lua 自己是一種腳本語言,咱們也在嘗試把代碼如何版本化存儲,直接內部驅動 Lua 代碼更新上線而不須要重啓 Nginx。

彈性化

咱們全部應用業務都接入了 Docker 容器,存儲仍是物理機;咱們會製做一些基礎鏡像,把須要的軟件打成鏡像,這樣不用每次去運維那安裝部署軟件了;將來能夠支持自動擴容,好比按照 CPU 或帶寬自動擴容機器,目前京東一些業務支持一分鐘自動擴容,下個月會進行彈性調度嘗試。

降級開關

一個前端提供服務的系統必須考慮降級,推送服務器推送降級開關,開關集中化維護,而後經過推送機制推送到各個服務器;

可降級的多級讀服務,前端數據集羣—-> 數據異構集羣—-> 動態服務 (調用依賴系統);這樣能夠保證服務質量,假設前端數據集羣壞了一個磁盤,還能夠回源到數據異構集羣獲取數據;基本不怕磁盤壞或一些機器故障、或者機架故障。

開關前置化,如 Nginx 代替 Tomcat,在 Nginx 上作開關,請求就到不了後端,減小後端壓力;咱們目前不少開關都是在 Nginx 上。

可降級的業務線程池隔離,從 Servlet3 開始支持異步模型,Tomcat7/Jetty8 開始支持,相同的概念是 Jetty6 的 Continuations。咱們能夠把處理過程分解爲一個個的事件。

經過這種將請求劃分爲事件方式咱們能夠進行更多的控制。如,咱們能夠爲不一樣的業務再創建不一樣的線程池進行控制:即咱們只依賴 tomcat 線程池進行請求的解析,對於請求的處理咱們交給咱們本身的線程池去完成;這樣 tomcat 線程池就不是咱們的瓶頸,形成如今沒法優化的情況。經過使用這種異步化事件模型,咱們能夠提升總體的吞吐量,不讓慢速的 A 業務處理影響到其餘業務處理。慢的仍是慢,可是不影響其餘的業務。咱們經過這種機制還能夠把 tomcat 線程池的監控拿出來,出問題時能夠直接清空業務線程池,另外還能夠自定義任務隊列來支持一些特殊的業務。

去年使用的是 JDK7+Tomcat7 最近一個月咱們升級到了 JDK8+Tomcat8+G1。

 

 

多機房多活

對於咱們這種核心系統,咱們須要考慮多機房多活的問題。目前是應用無狀態,經過在配置文件中配置各自機房的數據集羣來完成數據讀取。

 

 

其實咱們系統只要存儲多機房就多活了,由於系統自然就是。數據集羣採用一主三從結構,防止當一個機房掛了,另外一個機房壓力大產生抖動。

 

 

各個機房都是讀本機房的從另外每一個機房都是倆份數據,不怕由於機房忽然中斷恢復後的影響。

多種壓測方案

咱們在驗證系統時須要進行壓測。

線下壓測,Apache ab,Apache Jmeter,這種方式是固定 url 壓測,通常經過訪問日誌收集一些 url 進行壓測,能夠簡單壓測單機峯值吞吐量,可是不能做爲最終的壓測結果,由於這種壓測會存在熱點問題;

線上壓測,能夠使用 Tcpcopy 直接把線上流量導入到壓測服務器,這種方式能夠壓測出機器的性能,並且能夠把流量放大,也能夠使用 Nginx+Lua 協程機制把流量分發到多臺壓測服務器,或者直接在頁面埋點,讓用戶壓測,此種壓測方式能夠不給用戶返回內容。服務剛開始的時候大量使用 tcpcopy 作驗證,對於一些新服務,若是沒法使用 tcpcopy 咱們就在頁面埋 url 讓用戶來壓。

另外壓測時,要考慮讀、寫、讀或寫同時壓。只壓某一種場景可能都會不真實。

遇到的一些問題和解決方案

SSD 性能差

使用 SSD 作 KV 存儲時發現磁盤 IO 很是低。配置成 RAID10 的性能只有 36MB/s;配置成 RAID0 的性能有 130MB/s,系統中沒有發現 CPU,MEM,中斷等瓶頸。一臺服務器從 RAID1 改爲 RAID0 後,性能只有~ 60MB/s。這說明咱們用的 SSD 盤性能不穩定。

據以上現象,初步懷疑如下幾點:SSD 盤,線上系統用的三星 840Pro 是消費級硬盤;RAID 卡設置,Write back 和 Write through 策略(後來測試驗證,有影響,但不是關鍵);RAID 卡類型,線上系統用的是 LSI 2008,比較陳舊。

下面是使用 dd 作的簡單測試。

 

 

咱們現實居然使用的是民用級盤, 一個月壞幾塊很正常。後來咱們所有申請換成了 INTEL 企業級盤,線上用 3500 型號。

鍵值存儲選型壓測

在系統設計初期最頭痛的就是存儲選型,咱們對於存儲選型時嘗試過 LevelDB、RocksDB、BeansDB、LMDB、Riak 等,最終根據咱們的需求選擇了 LMDB。

機器:2 臺

配置:32 核 CPU、32GB 內存、SSD((512GB) 三星 840Pro—> (600GB)Intel 3500 /Intel S3610)

數據:1.7 億數據(800 多 G 數據)、大小 5~30KB 左右

KV 存儲引擎:LevelDB、RocksDB、LMDB,每臺啓動 2 個實例

壓測工具:tcpcopy 直接線上導流

壓測用例:隨機寫 + 隨機讀

LevelDB 壓測時,隨機讀 + 隨機寫會產生抖動(咱們的數據出自本身的監控平臺,分鐘級採樣)。

 

 

咱們線上一些順序寫的服務在使用 leveldb,RocksDB 是改造自 LevelDB,對 SSD 作了優化,咱們壓測時單獨寫或讀,性能很是好,可是讀寫混合時就會由於歸併產生抖動。

 

 

在歸併時基本達到了咱們磁盤的瓶頸,LMDB 引擎沒有大的抖動,基本知足咱們的需求。

 

 

咱們目前一些線上服務器使用的是 LMDB,新機房正在嘗試公司自主研發的 CycleDB 引擎。目前我看到的應該不多使用 LMDB 引擎的。

數據量大時 Jimdb 同步不動

Jimdb 數據同步時要 dump 數據,SSD 盤容量用了 50% 以上,dump 到同一塊磁盤容量不足。

解決方案:

一臺物理機掛 2 塊 SSD(512GB),單掛 raid0;啓動 8 個 jimdb 實例;這樣每實例差很少 125GB 左右;目前是掛 4 塊,raid0;新機房計劃 8 塊 raid10;

目前是千兆網卡同步,同步峯值在 100MB/s 左右;

dump 和 sync 數據時是順序讀寫,所以掛一塊 SAS 盤專門來同步數據;

使用文件鎖保證一臺物理機多個實例同時只有一個 dump;

後續計劃改造爲直接內存轉發而不作 dump。

切換主從

由於是基於 Redis 的,目前是先作數據 RDB dump 而後同步。後續計劃改造爲直接內存複製,以前存儲架構是一主二從(主機房一主一從,備機房一從)切換到備機房時,只有一個主服務,讀寫壓力大時有抖動,所以咱們改造爲以前架構圖中的一主三從。

分片配置

以前的架構是存儲集羣的分片邏輯分散到多個子系統的配置文件中,切換時須要操做不少系統。

解決方案:

引入 Twemproxy 中間件,咱們使用本地部署的 Twemproxy 來維護分片邏輯;

使用自動部署系統推送配置和重啓應用,重啓以前暫停 mq 消費保證數據一致性;

用 unix domain socket 減小鏈接數和端口占用不釋放啓動不了服務的問題。

咱們都是在應用本地部署的 Twemproxy,而後經過中間系統對外提供數據。

模板元數據存儲 HTML

咱們前端應用使用的是 Nginx+Lua,起初不肯定 Lua 作邏輯和渲染模板性能如何,就儘可能減小 for、if/else 之類的邏輯;經過 java

worker 組裝 html 片斷存儲到 jimdb,html 片斷會存儲諸多問題,假設將來變了也是須要全量刷出的,所以存儲的內容最好就是元數據。

所以經過線上不斷壓測,最終 jimdb 只存儲元數據,lua 作邏輯和渲染;邏輯代碼在 3000 行以上;模板代碼 1500 行以上,其中大量 for、if/else,目前渲染性能夠接受。

線上真實流量,總體性能從 TOP99 53ms 降到 32ms。

 

 

綁定 8 CPU 測試的,渲染模板的性能能夠接受。

 

 

庫存接口訪問量 600W / 分鐘

商品詳情頁庫存接口 2014 年被惡意刷,每分鐘超過 600w 訪問量,tomcat 機器只能定時重啓;由於是詳情頁展現的數據,緩存幾秒鐘是能夠接受的,所以開啓 nginx proxy

cache 來解決該問題,開啓後降到正常水平;咱們目前正在使用 Nginx+Lua 架構改造服務,數據過濾、URL 重寫等在 Nginx 層完成,經過 URL 重寫 + 一致性哈希負載均衡,不怕隨機 URL,一些服務提高了 10%+ 的緩存命中率。

目前咱們大量使用內存級 nginx proxy cache 和 nginx 共享字典作數據緩存。

http://c.3.cn/recommend?callback=jQuery4132621&methods=accessories%2Csuit&p=103003&sku=1217499&cat=9987%2C653%2C655&lid=1&uuid=1156941855&pin=zhangkaitao1987&ck=pin%2CipLocation%2Catw%2Caview&lim=6&cuuid=1156941855&csid=122270672.4.1156941855%7C91.1440679162&c1=9987&c2=653&c3=655&_=1440679196326

還有咱們會對這些前端的 url 進行重寫,因此無論怎麼加隨機數,都不會影響咱們服務端的命中率,咱們服務端作了參數的從新拼裝和驗證。

微信接口調用量暴增

14 年的一段時間微信接口調用量暴增,經過訪問日誌發現某 IP 頻繁抓取;並且按照商品編號遍歷,可是會有一些不存在的編號。

解決方案:

讀取 KV 存儲的部分不限流;

回源到服務接口的進行請求限流,保證服務質量。

回源到 DB 的或者依賴系統的,咱們也只能經過限流保證服務質量。

開啓 Nginx Proxy Cache 性能不升反降

開啓 Nginx Proxy Cache 後,性能降低,並且過一段內存使用率到達 98%。

解決方案:

對於內存佔用率高的問題是內核問題,內核使用 LRU 機制,自己不是問題,不過能夠經過修改內核參數

sysctl -w vm.extra_free_kbytes=6436787

sysctl -w vm.vfs_cache_pressure=10000

使用 Proxy Cache 在機械盤上性能差能夠經過 tmpfs 緩存或 nginx 共享字典緩存元數據,或者使用 SSD,咱們目前使用內存文件系統。

配送至讀服務因依賴太多,響應時間偏慢

配送至服務天天有數十億調用量,響應時間偏慢。

解決方案:

串行獲取變併發獲取,這樣一些服務能夠併發調用,在咱們某個系統中能提高一倍多的性能,從原來 TP99 差很少 1s 降到 500ms 如下;

預取依賴數據回傳,這種機制還一個好處,好比咱們依賴三個下游服務,而這三個服務都須要商品數據,那麼咱們能夠在當前服務中取數據,而後回傳給他們,這樣能夠減小下游系統的商品服務調用量,若是沒有傳,那麼下游服務再本身查一下。

假設一個讀服務是須要以下數據:

數據 A 10ms

數據 B 15ms

數據 C  20ms

數據 D  5ms

數據 E  10ms

那麼若是串行獲取那麼須要:60ms;而若是數據 C 依賴數據 A 和數據 B、數據 D 誰也不依賴、數據 E 依賴數據 C;那麼咱們能夠這樣子來獲取數據:

 

 

那麼若是併發化獲取那麼須要:30ms;能提高一倍的性能。

假設數據 E 還依賴數據 F(5ms),而數據 F 是在數據 E 服務中獲取的,此時就能夠考慮在此服務中在取數據 A/B/D 時預取數據 F,那麼總體性能就變爲了:25ms。

咱們目前大量使用併發獲取和預取數據,經過這種優化咱們服務提高了差很少 10ms 性能。 並且能顯著減小一些依賴服務的重複調用,給他們減流。

 

 

以下服務是在抖動時的性能,老服務 TOP99 211ms,新服務 118ms,此處咱們主要就是併發調用 + 超時時間限制,超時直接降級。

 

 

網絡抖動時,返回 502 錯誤

Twemproxy 配置的 timeout 時間太長,以前設置爲 5s,並且沒有分別針對鏈接、讀、寫設置超時。後來咱們減小超時時間,內網設置在 150ms 之內,當超時時訪問動態服務。對於讀服務的話,應該設置合理的超時時間,好比超時了直接降級。

機器流量太大

2014 年雙 11 期間,服務器網卡流量到了 400Mbps,CPU30% 左右。緣由是咱們全部壓縮都在接入層完成,所以接入層再也不傳入相關請求頭到應用,隨着流量的增大,接入層壓力過大,所以咱們把壓縮下方到各個業務應用,添加了相應的請求頭,Nginx

GZIP 壓縮級別在 2~4 吞吐量最高;應用服務器流量降了差很少 5 倍;目前正常狀況 CPU 在 4% 如下。

由於以前壓縮都是接入層作的,後來由於接入的服務太多,所以咱們決定在各應用去作。

 

 

一些總結

數據閉環

數據維度化

拆分系統

Worker 無狀態化 + 任務化

異步化 + 併發化

多級緩存化

動態化

彈性化

降級開關

多機房多活

多種壓測方案

Nginx 接入層線上灰度引流

接入層轉發時只保留有用請求頭

使用不須要 cookie 的無狀態域名(如 c.3.cn),減小入口帶寬

Nginx Proxy Cache 只緩存有效數據,如託底數據不緩存

使用非阻塞鎖應對 local cache 失效時突發請求到後端應用 (lua-resty-lock/proxy_cache_lock)

使用 Twemproxy 減小 Redis 鏈接數

使用 unix domain socket 套接字減小本機 TCP 鏈接數

設置合理的超時時間(鏈接、讀、寫)

使用長鏈接減小內部服務的鏈接數

去數據庫依賴(協調部門遷移數據庫是很痛苦的,目前內部使用機房域名而不是 ip),服務化

客戶端同域鏈接限制,進行域名分區:c0.3.cn c1.3.cn,若是將來支持 HTTP/2.0 的話,就再也不適用了。

Q&A

Q1:對於依賴服務的波動,致使咱們系統的不穩定,咱們是怎麼設計的?

咱們的數據源有三套:前端數據集羣 該數據每一個機房有兩套,目前兩個機房。數據異構集羣同上動態服務 (調用依賴系統)。

設置好超時時間,尤爲鏈接超時,內網咱們通常 100ms 左右;

每一個機房讀從、若是從掛了降級讀主;

若是前端集羣掛了,咱們會讀取動態服務(一、先 mget 原子數據異構集羣;二、失敗了讀依賴系統)。

Q2:靜態化屏蔽經過 js 是怎麼作的?

緊急上線 js,作跳轉;

上線走流程須要差很少 10 分鐘,而後還有清理 CDN 緩存,用戶端還有本地緩存沒法清理;

咱們目前文件都放到咱們的自動部署上,出問題直接修改,而後同步上去,重啓 nginx 搞定。

Q3:內網的服務經過什麼方式提供?

我偏好使用 HTTP,目前也在學習 HTTP2.0,有一些服務使用咱們本身開發相似於 DUBBO 的服務化框架,以前的版本就是 DUBBO 改造的。

Q4:對於 mq 的處理若是出現異常是怎麼發現和處理的?

MQ,咱們目前是接收下來存 Redis,而後會寫一份到本地磁盤文件;

咱們會把消息存一天的;

通常出問題是投訴,咱們會緊急回滾消息到一天中的某個時間點。

Q5:對於模板這塊,有作預編譯處理?或者直接使用 Lua 寫模板嗎?

luajit,相似於 java jit;

lua 直接寫模板。

Q6: jimdb 可否介紹下,特性是什麼,爲何選用?

jimdb 就是咱們起的一個名字,以前版本就是 redis+lmdb 持久化引擎,作了持久化;

咱們根據當時的壓測結果選擇的,按照對比結果選擇的,咱們當時也測了豆瓣的 beansdb,性能很是好,就是須要按期人工歸併。

Q7:諮詢下對於價格這類敏感數據,前端有緩存麼?仍是都靠價格服務扛?

價格前端不緩存;

價格實時同步到 Nginx+Lua 本地的 redis 集羣(大內存);

不命中的纔回源到 tomcat 查主 redis/DB。

價格數據也是經過 MQ 獲得變動存儲到本地 redis 的,nginx+lua 直接讀本機 redis,性能沒的說。

Q8:庫存和價格同樣的模式處理嗎?

前端展現庫存不是,咱們作了幾秒的服務端緩存。

Q9:github 裏有一個開源的基於 lmdb 的 redis 大家用的這個嗎?

咱們內部本身寫的,有一版基於 LevelDB 的,我記得 github 上叫 ardb。

Q10:看測試條件說測試的是大小 5~30KB 左右的數據,有沒有測試過更大文件 lmdb 的表現?

這個沒有,咱們的數據都是真實數據,最大的有 50KB 左右的,可是分佈比較均勻;當時還考慮壓縮,可是發現沒什麼性能問題,就沒有壓縮作存儲了。

Q11:關於 redis 緩存,是每一個子系統擁有本身的一套緩存;仍是使用統一的緩存服務?是否有進行過對比測試?(看到又說使用單機緩存防止服務掛掉,影響總體服務)

咱們公司有統一的緩存服務接入並提供運維;

咱們的服務本身運維,由於咱們是多機房從,每機房讀本身的從,目前不支持這種方式;

(看到又說使用單機緩存防止服務掛掉,影響總體服務)這個主要是降級 + 備份 + 回源解決。

Q12: 「咱們目前一些線上服務器使用的是 LMDB,其餘一些正在嘗試公司自主研發的 CycleDB 引擎」。 開始自主研發,這個是因爲 lmdb 有坑仍是處於別的考慮?

寫放大問題;

掛主從須要 dump 整個文件進行同步。

----------------------------------------------------------------------------------------------

 

 
楊 超/3.2 大促系統全流量壓測及穩定性保證——京東交易架構.232
3.2.1 交易系統的三個階段232
3.2.2 交易系統的三層結構233
3.2.3 交易系統的訪問特徵234
3.2.4 應對大促的第1 步:全鏈路全流量線上壓測.234
3.2.5 應對大促的第2 步:根據壓力錶現進行調優.237
3.2.6 異步和異構240
3.2.7 應對大促的第3 步:分流與限流242
3.2.8 應對大促的第4 步:容災降級.244
3.2.9 應對大促的第5 步:完善監控.245
3.2.10 疑問與解惑246

https://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=2653547431&idx=1&sn=744a42639e7c362a05aacbfbed6a988c&scene=0#wechat_redirect

你們好!我是來自京東商城交易平臺的楊超,今天特別高興可以來給你們分享每一年 618 及雙十一所作過的工做,我是 2011 年加入京東,在這 5 年中我經歷了很多技術演進,也看到了很多變化,在這裏給你們作一個分享。

 

先介紹一下交易系統基本狀況。

 

交易系統的三階段

 


(點擊圖片全屏縮放)

 

這張圖是整個京東商城數據流向結構。這個圖主要分爲三個部分。

 

  1. 訂單生成前,包括單品頁,購物車,架構,促銷等功能,咱們每一個用戶進來須要訪問。它的特色是大促期間訪問量很是大,後面會詳細介紹如何應對。

  2. 訂單預處理,訂單生成以後,這是一個原始生成單,以後須要對訂單進行預處理,進行拆包,包裹的拆分、你們電小家電拆開包裹運送等。由於是統一下單,這一塊是訂單預處理。挑戰是訪問量大,各個模塊如拆單、訂單轉移,支付臺賬等可能會承受很是大的壓力,咱們會採起擴容存儲,限流,數據結構優化等方法去應對

  3. 訂單履約階段,真正到後面是整個處理過程,配送黏合起來作的系統。這是京東商城的服務結構。

 

交易系統的三層結構

 

下面是交易結構圖,從左到右,單品頁是網站平臺作的,今天來到現場的也有一些參與過的同事。移動端、微信、手Q,等入口都會調到購物車服務。

 

從購物車開始,咱們從上到下是一個典型的分層結構,上面是調用來源,中間是咱們的服務,下面是依賴的底層服務。其中強依賴服務是關鍵路徑所須要調用的服務,是主流程中不可缺乏的一部分。

 


(點擊圖片全屏縮放)

 

強依賴服務在大促期間不能被降級,咱們須要提早擴容,以及進行代碼重構、拆分、按來源單獨部署等方法提早進行優化。

 

交易系統的訪問特徵

 

爲了更好應對,咱們須要對用戶訪問的特色進行分析。參看下面圖,2011 年到 2015 年整個的單量,2011 年,618 是幾十萬的單量,去年單量一天是幾千萬。看着單量往上增加的這張圖,就能感覺到系統壓力有多大。

 

爲了應對大促的壓力,咱們必須清楚知道用戶訪問系統功能的流量及分佈。通過數據統計,接單前面這波系統,正常每一單,有一個前幾年的大概的統計。購物車:結算頁:產生訂單頁面訪問的比例是 16 :4 :1,也就是說購物車訪問 16 次,結算頁訪問 4 次,提交訂單頁面訪問 1 次。到 618 及雙十一,天天 PV 就是幾十億,幾百億、上千億,所以我看到最大的量 1 分鐘是幾千萬。我須要清楚知道這幾千訪問落到那幾個頁面。

 

根據京東傳統,每一年定一個目標,618 當天或者三天須要達到多少億的指標,好比說一百億或者幾百億,後面咱們會把錢換算成咱們的單量,咱們客單價是多少?若是客單價是 300,目標要 100 億,則咱們單量須要達到百萬或者三千萬,這樣經過預估出來當天的單量會有多少,這是提早的準備的總體規劃過程

根據GMV目標預估訂單量 ~

 

爲了每一年的 61八、雙十一系統的穩定,京東研發如何應對?

 

應對大促的第一步

全鏈路全流量線上壓測

 

系統底層的調用量是知道的,往年的 618 或者往年的雙十一也能夠找到。通過半年的業務跟進,咱們系統會有不少的變動,數據變動或者是代碼變動結構變動都會產生,咱們知道這個系統可以承受多大量,上來對它進行壓測。

 

壓測分爲線上壓測、線下壓測,主力作線上壓測

 

爲何咱們會採用線上壓測?早年咱們只作線下壓測,環境跟線上不同,路由器和機器 CPU,物理機,每個不相同或者架設的路由超過 3 層,丟包,各類數據不同,壓測出來的數據常常會差別。

 

線上壓測分開是怎麼樣作的?須要將讀業務跟寫業務區分開。讀業務,咱們正常能夠看到讀價格讀庫存、讀購物車場景的分開,讀跟寫,看到購物車上的分佈,就能知道是讀仍是寫。

 

演練縮減服務器

 

從壓測上,在集羣中將服務器縮減,由於咱們支撐的量,最高量達到 1 分鐘達到 1 億左右,日常最少有幾十萬、幾百萬的量。集羣確定是比較大的,最少也是幾十臺的機器,咱們會把集羣機器逐臺往下縮減,真正看到線上量能扛到什麼狀況。

 

作到這兒,你們會有疑問?風險挺大。對,風險的確挺大,好比一個集羣的 30 臺機器一個一個往下縮,好比縮到 5 臺,若是扛不住,全部的機器就崩潰,就會面臨很大風險。因此梳理完每一個架構以後,每一年咱們冒着風險,找到這個點,往上一點的量進行縮減,縮到必定程度再強行縮。

 

複製流量

 

主要經過 TCPCopy 複製端口流量,多層翻倍放大流量。以下圖就直接將每層流量翻倍總體就是 1,000 倍,工具實現簡單,能夠實現多條線組合進行流量複製。經過這種方式發起超負荷的請求,檢驗服務可以承載的容量。

 

 

模擬流量

 

咱們作了一個成立了一個壓力小組,線上壓力測試小組,咱們作線上壓測。用很是簡單的底層工具去作壓測。底層發起的量特別快並且特別多,集羣,咱們只作了壓測平臺,把這些工具集成起來作模擬流量壓測。

 

在數據模擬上,咱們是本身事先會準備一批數據。好比說幾萬個用戶,幾萬個 SKU。商家各類庫存模式,促銷模式,在模擬平臺咱們會準備好。

 

流量泄洪

 

咱們把訂單在這個結構,接住堵在這個地方不往下放,日後拽都是密集的一些服務。從這一塊把量堵住,堵幾十萬,忽然有一天打開,看到一個峯值,看每一分鐘處理量,日後能承受多大量,是否是可以承受發起的量,

 

實施方法

 

你們可能在朋友圈看到照片,各個服務的核心人員,集中在一個會議室,進行壓測。一步一步往上加量,嚴密監控線上響應狀況、訂單量狀況、各個服務器,以及各個緩存、數據庫等機器的實際負載狀況。出現任何風吹草動就中止發起壓力,並進行記錄和排查問題。

 

 

而後壓測訂單提交,往主集羣寫數據。跟購物車不一樣,這種壓測會直接在生成集羣上進行壓測,並會寫入數據。所以須要將寫入數據進行隔離操做,並將垃圾數據進行數據刪除,不能進入生產環境。

 

根據業務和技術維度篩選一批商品、一批用戶,主要覆蓋存儲分佈、用戶每一個等級以及業務分支。促銷組幫忙創建能覆蓋全部環節的促銷數據。將這些用戶的提交訂單後清空購物車的功能禁用,保證能不停的重複下單。另外這些用戶的訂單提交流程中的郵件、短信提醒等相關功能禁用,產生的訂單進行隔離,不往生產系統下發,並在測試完成後進行刪除

至關於重建一個 test 環境配置,和 prod 環境並立~

 

線上壓測時,組織各個相關組核心人員嚴密監控各項數據。出現問題當即中止壓測。先進行恢復,同時進行數據記錄和問題排查,如分鐘級沒法恢復則直接切亦莊備用集羣。

 

每一個服務分別進行一輪壓測,記錄每一個服務和購物車、訂單提交壓測得出的數據。根據線上實際用戶調用比例進行換算,得出一個相對精準的總體集羣承載數據。

 

訂單生產後系統,主要用憋單,快速釋放流量進行壓測。造成對整個後續系統的,持續性高流量衝擊,得出總體系統的處理訂單能力。

 

下面是壓測的 DPMP 系統結構圖。

 

 

經過壓測,就知道目前京東系統,壓測完能承受多大量,面臨咱們目標差距有多少?壓測完以後,咱們就要運維優化。

 

 

在打壓時候,咱們按照交易系統的流量分佈來模擬流量,好比正常訪問購物車與結算頁是 16 :4 的流量,下圖的在打壓時候咱們也嚴格按照這個流量來執行,確保壓力接近大促時候的真實訪問場景。

 

 

 

應對大促的第二步

根據壓力錶現進行調優

 

調優一:多級緩存

 

緩存從前面比較多,CDN、Nginx、Java 都會有緩存。

 

緩存是逐級往下作,是一個漏斗狀,最開始作緩存,到緩存的持續性在很短的時間內,一分鐘或者一秒鐘或者毫秒,這樣給用戶的感知是看不到緩存的,若是你要承載這麼大量,必須逐級作緩存,前面作一些靜態緩存掉,後面會作一些基礎數據緩存,最後大數據,一層一層往上能擋住整個這一塊

 

 

 

調優二:善用異步

 

這是購物車大概的結構。這裏有一個異步雙寫,咱們會寫丟這個數據,寫丟不要緊,咱們購物車是總體的,加一個商品,寫不過來,下次過來又會全覆蓋。這樣購物車就有一個多機房多活的可用性

 

 

調優三:超熱數據的緩存

 

購物車裏面作熱數據緩存,這種數據的緩存,好比促銷服務直接影響到價格,緩存效率必須是在秒級毫秒級,在一秒鐘怎麼篩選十億商品裏面最熱的商品?

 

咱們利用 Queue 的原理,不斷往裏塞 SKU,隊列的長只有 50。傳進來以後,這裏有的位置往前移,咱們很快知道在一秒鐘知道,排在前面確定是訪問次數最多的,每個階段應用存儲訪問最多的數據,若是是秒殺商品,500 萬的請求有十萬到二十萬,它確定大部分的請求在這塊就出去了,不會穿透進來,這是咱們本身作的熱數據緩存。

 (?~)

 

 

調優四:數據壓縮

 

對 Redis 存儲的數據進行壓縮,這樣空間又縮小四分之一或是三分之一,咱們數據到後面就會很小。當量小以後,訪問效率就會升高,你數據量彈出很小,丟單率很小,能夠提升咱們的可用性。

 

異步和異構

 

什麼是異步?購物車會調三個服務,若是是串行,一個 2 毫秒,累計起來是 6 毫秒。同時去處理,同時日後並行,這三個服務同時去調,根據業務規則要先調商品,再調促銷,調兩次,確定在三毫秒以內。

 

異步數據落地,中間存儲來解決訪問流量過大,衝擊原始存儲的問題。包括庫存狀態庫存數據的剝離,庫存狀態調用量太大,穿透直接到庫存數據。

 

接單系統的異構

 

異步異構用得最多在這個系統裏面。這一步接單系統的異構。接單系統在這一塊的異構。咱們整個接單,一次性提交訂單、購物車是提交成一份數據,這樣提升效率

 

若是說按照原來的作法,直接寫到表裏面,有不少訂單明細、促銷明細、優惠券不少要寫,這樣訪問效率會存在瓶頸。所以後面寫到接單服務,再異步,調動某一個狀態機,經過管道服務再衍生出來拆分紅訂單中心數據,支付臺帳,異構以後,單個系統的應對峯值的能力都獲得了提高

 

 

訂單中心的異構

 

提交訂單、接單作了異步處理。

 

再往下訂單中心又會有很大異構,分紅了 4 個子系統去分別調用。訂單中心會產生列表服務數據,列表服務數據根據PIN的維度用戶維度看到數據存儲,第一步直接寫,寫不成功就是狀態機,異步寫到這一塊存儲。

 

訂單中心有一個列表服務的存儲,再有訂單詳情的存儲,訂單詳情的存儲是根據訂單維度去存,這一塊是根據 PIN 存的。

 

第3、四部分是單拎出來的狀態服務及跟蹤服務,後續生產跟它直接掛鉤。經過異構以後,提升了訂單中心應對峯值的能力。

 

商品頁的異構

 

後面看一下看不到商品服務的一切異構。ERP 過來,是採銷或者 POP 用戶,進入咱們的 MySQL,經過商品發佈系統,接到商品消息在這一塊以前是不能售賣,這一塊直接發出去,這個消息會寫自身的存儲,咱們會裝 A 包、B 包、C包。

 

  • A 包就是基礎數據,好比商品名稱;

  • B 包擴充數據;

  • C 包好比特殊標識,生鮮、易碎。

 

這樣能夠把數據分開,由於數據量太大了,十個億的數據就有幾百個 G,分開幾個包。這樣性能和可擴展性都獲得了提高。

 

商品服務調用方,我只要調,訂單系統查你,我知道是生鮮、易碎。可是前面須要基礎數據,商品的名字,特殊屬性,只有訂單結算頁須要,因此分這麼多的包,我能夠分開部署商品服務。爲何異構這些包。

 

發一個通知給別的系統,大部分系統依賴基礎服務,發一個消息給他,商品變動了,它會緩存本身須要的數據,實時計算經過這個地方過來,異構出來存儲,內部異構出來這麼多存儲,這是後臺用戶存儲。

 

一個商品服務,真正作大了能夠參考對一個胖的大系統進行拆分的異構方法,若是達不到這種規模以前也不建議過細去分。

 

 

應對大促的第三步

分流與限流

 

咱們假設系統當超過必定流量後,超過的流量作直接拒絕處理,以便保護後端的服務,這就是限流。

 

Web 的限流根據 PIN 來限流,這是根據 IP 加 PIN 風控數據限流,這一塊根據業務邏輯,一個單一天能下多少單,根據這個邏輯去限流。渠道能夠按 App、PC、微信等分開,分流和限流這麼作。

 

 

下面講秒殺系統是怎麼來的。秒殺系統是限流和分流的典型。

 

秒殺,假設預定是 1500 萬,在那一分鐘以內,這麼多用戶過來搶手機,也就是單個商品,就把流量直接導到秒殺系統

 

秒殺系統從 Ngnix 進來就有各類的限制,到咱們會識別用戶供應商或者商販去刷的數據,這塊調用是從正常訪問的單品頁分出來,不影響主流程

 

經過 IP、PIN、每一步怎麼來、用戶以提交記錄,一秒鐘提交多少次,一分鐘提交多少次等一堆的規則作判斷來限流。到最後再驗證有沒有預定、經常使用地址服務等,都經過後再調到接單系統。

 


整個秒殺系統就是一個典型的沙漏的系統,當流量跑到後面,實際上只剩很小的一部分,只有真實的寫流量到接單。

 

接單提交服務單獨出來兩臺機器給它用,後面的存儲獲得保護,兩臺機器最多也就幾十萬,也能承載住,這就是分流跟限流。

 

促銷與價格

 

促銷裏面也有一個限購,好比前 30 個用戶享受促銷,發一個碼出去,須要對這個碼進行處理,這是一種限流。

 

促銷分流中須要把價格服務單拎出來,分出去,單品頁搜索,手機微信,購物車的架構從這裏出來,最實時的價格。這樣產生分流,這一塊有一個存儲分流,還有更多其它的就沒有一一列舉,這只是一個示意圖。

 

這就是咱們整個的分流跟限流。根據前面的渠道,調用量、作多少程度,相對於影響力,作分流和限流。

 

 

應對大促的第四步

容災降級

 

若是分流、限流還沒抗住,系統進一步出現壓力問題,再要作準備作容災降級。

 

容災降級有機房容災,咱們作多中心機房,網絡容災、內網外網的容災,應用的容災,分組、託底容器,最後保證基礎的服務是正常的。

 

網絡及 IDC 降級

 

這是容災降級,這是網絡大概示意圖。咱們的 ISP 進入機房,核心交換機、櫃頂交換機、這是交換級的容災,網絡共享容災。

 

 

業務降級

 

購物車結算頁的降級,當訂單出現過大,延保服務、預定服務若是不行,直接保主流層,就屬於業務層面的降級。

 

(點擊圖片全屏縮放)

 

整個 61八、雙十一準備下來了,後面準備降級方案、容災方案列出不少,每一個業務須要根據本身的狀況去考慮,上面只是簡單列舉了幾個供你們參考。

 

 

應對大促的最後一步

完善監控

 

最後到臨近雙11、618,須要網絡監控、機器監控、以及瞭解訂單量、登陸量、註冊量等應用級的監控。

 

這是 IDC 機房的監控,運維部作的。這是單個機器,物理機、Docker 的監控。以及對交換機、IP 進行監控,這是網絡監控。

 

下面是方法監控,每一個方法監控到 TP99,99 是最近幾回的峯值是達到多少毫秒?成功率、失誤率、調用次數等。

 

 

訂單量的監控,一旦出問題都會報警,這是大概的監控系統,這是依賴的一些監控系統,咱們還會衍生出來本身應用的監控。好比說庫存,預算是最重要,提交訂單,保證提交訂單成功是最重要,從那一堆,庫存本身的一套寫一個頁面,寫一套監控系統出來,優惠券、購物車寫一個小監控系統去監控。監控是到大促的眼睛。

 

 

一次大促,總結下來就這麼多,謝謝你們!

 

Q&A

 

Q:您提到作線上的壓測,會產生不少髒數據,這個數據是最終怎麼處理。還有一個,您作了好多異構的數據,這個數據怎麼保持它的一致性。

楊超:先回答你髒數據,所謂的髒數據,寫數據是爲了隔離出來,有的是打標,有的是另外起表,在數據庫裏面把這些數據隔閡開來打標,一邊寫一邊刪,壓測,30分鐘到一個小時,咱們是在凌晨作這些事情,壓測完寫的數據是很是危險,寫的數據會爆,引發癱瘓。咱們實時監控它,打標清理,首先切到小集羣承載。真實的集羣,真正的量不會達到618那個量,咱們的峯值可能會很高,正常到晚上凌晨那個量很小,咱們就把它切到另一個小集羣承載,後面再把它轉回來。

(?~)

 

異構出來的數據怎麼保證它完整性一致性?異構都是小維度,不成功會補全,補全不成功會穿透,寫 Ngnix,會一層一層穿透到 MySQL 數據庫。(?~)

 

Q:一個庫存性能與一致性的問題,客戶下單確定會判斷一下有沒有庫存,而且他下單的時候還要實時扣減庫存,這方面怎麼解決性能問題?若是把庫存的量放在 MySQL,一拿會慢也會影響你的下單,而且渠道會比較多。

楊超:庫存大部分用前端 Redis 防重。用業務維度作防重,第一次查出來的業務屬性、商品屬性、庫存數量數據,咱們的一些業務數據、一致性查出來,進行一次校驗,校驗成功就經過,校驗不成功就告訴它庫存不足。目前看到這個單量一天幾千萬單,幾乎沒有超賣的部分。

(因此意思是否是徹底精確的~)

 

Q:SKU 庫存量是放在 Redis,經過 Redis 實時判斷?

楊超:對,基本上卡的第一道都在前面,查的第一項,若是查到數據就會寫到 Redis,最後 MySQL 是落地存儲,內部存儲。MySQL 寫入量最大達到 60、70 萬。

 

Q:還有一個問題關於線上壓測,還有線下壓測。線下壓測指的是大家把流量鏡像到相同環境裏面實時作壓測仍是走的同一個環境?

楊超:線下一個測試機房,拿一堆機器按照你線上結構部署,導一點數據下來進行壓測。線上是咱們真實佈局的線上的環境,配置差很少同樣

 

Q:數據庫對應的客戶可否看到你的壓測數據?

楊超:壓測的訂單,從接單到後面就會把它屏蔽,直接截住,相關數據要打一個標,正常數據打一個標識,不能往下傳,不能讓客戶看到

---------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------

 


呂 毅/3.3 秒殺系統架構解密與防刷設計.248
3.3.1 搶購業務介紹.248
3.3.2 具體搶購項目中的設計.249
3.3.3 如何解耦先後端壓力250
3.3.4 如何保證商品庫的庫存可靠252
3.3.5 如何與第三方多方對帳.254
3.3.6 項目總結255
3.3.7 疑問與解惑255

---------------------------------------------------------------------------------------------------

「架構」 - 秒殺系統設計

對於系統設計須要從高維度出發,從總體上思考問題。

1、秒殺系統架構特色

  • 穩,系統高可用,PlanB方案兜底
  • 準,保證數據一致性,防止超賣
  • 快,系統高性能,鏈路協同優化,不存在明顯短板

2、秒殺系統特色

  • 讀多寫少,充分利用緩存(讀:減小用戶到服務端「讀」數據寫:DB層面獨立,作特殊處理)
  • 動靜分離,靜態文件CDN
  • MySQL批量插入,減小SQL解析次數、網絡IO,日誌
  • nginx優化配置
  • 系統預熱,峯值到達前,緩存都預加載完畢(運維小工具)
  • 防止雪崩效應,避免緩存失效不可控,制定兜底方案

3、秒殺系統設計

A、緩存(瀏覽器-->web server-->服務-->數據)

  • 瀏覽器緩存、CDN、NGINX
  • 先後端分離、靜態頁面緩存
  • 頁面級(列表頁、單品頁)、URL級、對象級(減小數據庫訪問)
  • 內存標記、減小redis訪問

B、異步處理

  • 消息隊列
  • 用戶體驗

C、削峯、限流、接口安全

  • 點擊查詢或者購買後,按鈕置灰,禁止重複提交請求
  • 秒殺接口地址隱藏,秒殺開始前,拿不到真實接口地址
  • 花式驗證碼(減輕系統壓力,削峯)
  • 接口防刷(限流)

D、其餘

  • 分佈式session
  • 壓力測試
  • nginx水平拓展

4、相關配置

A、nginx配置

worker_rlimit_nofile 10000;   //子進程容許打開的文件數
keepalive_timeout 0;          //結束響應後當即斷開tcp鏈接。
//keeplive_timeout表示tcp完成響應後,繼續保持鏈接多久。若是這個值過大,會形成資源無效佔用。
events{
    worker_connections 10240;
}
//系統層面
echo 500000 > /proc/sys/net/core/somaxconn  //設置最大鏈接數
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle  //加快tcp回收
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse    //空的tcp容許回收利用
echo 0 > /proc/sys/net/ipv4/tcp_syncookies  //關閉洪水抵禦

ulimit -n 30000

參考:

InfoQ - 促進軟件開發領域知識與創新的傳播

人類身份驗證 - SegmentFault

那些年咱們一塊兒追過的緩存寫法(一) - 蘑菇先生 - 博客園

iMouseWu/limiter

秒殺系統實現分析 - 趙伊凡's Blog

秒殺系統架構分析與實戰

編輯於 2019-04-22

---------------------------------------------------------------------------------------------------

 

【高併發】高併發秒殺系統架構解密,不是全部的秒殺都是秒殺!

 

摘自:http://www.noobyard.com/article/p-agfaxltw-ce.html

【高併發】高併發秒殺系統架構解密,不是全部的秒殺都是秒殺!

 

前言

不少小夥伴反饋說,高併發專題學了那麼久,可是,在真正作項目時,仍然不知道如何下手處理高併發業務場景!甚至不少小夥伴仍然停留在只是簡單的提供接口(CRUD)階段,不知道學習的併發知識如何運用到實際項目中,就更別提如何構建高併發系統了!

究竟什麼樣的系統算是高併發系統?今天,咱們就一塊兒解密高併發業務場景下典型的秒殺系統的架構,結合高併發專題下的其餘文章,學以至用。

電商系統架構

在電商領域,存在着典型的秒殺業務場景,那何謂秒殺場景呢。簡單的來講就是一件商品的購買人數遠遠大於這件商品的庫存,並且這件商品在很短的時間內就會被搶購一空。 好比每一年的61八、雙11大促,小米新品促銷等業務場景,就是典型的秒殺業務場景。

咱們能夠將電商系統的架構簡化成下圖所示。

003

由圖所示,咱們能夠簡單的將電商系統的核心層分爲:負載均衡層、應用層和持久層。接下來,咱們就預估下每一層的併發量。

  • 假如負載均衡層使用的是高性能的Nginx,則咱們能夠預估Nginx最大的併發度爲:10W+,這裏是以萬爲單位。

  • 假設應用層咱們使用的是Tomcat,而Tomcat的最大併發度能夠預估爲800左右,這裏是以百爲單位。

  • 假設持久層的緩存使用的是Redis,數據庫使用的是MySQL,MySQL的最大併發度能夠預估爲1000左右,以千爲單位。Redis的最大併發度能夠預估爲5W左右,以萬爲單位。

因此,負載均衡層、應用層和持久層各自的併發度是不一樣的,那麼,爲了提高系統的整體併發度和緩存,咱們一般能夠採起哪些方案呢?

(1)系統擴容

系統擴容包括垂直擴容和水平擴容,增長設備和機器配置,絕大多數的場景有效。

(2)緩存

本地緩存或者集中式緩存,減小網絡IO,基於內存讀取數據。大部分場景有效。

(3)讀寫分離

採用讀寫分離,分而治之,增長機器的並行處理能力。

秒殺系統的特色

對於秒殺系統來講,咱們能夠從業務和技術兩個角度來闡述其自身存在的一些特色。

秒殺系統的業務特色

這裏,咱們能夠使用12306網站來舉例,每一年春運時,12306網站的訪問量是很是大的,可是網站平時的訪問量倒是比較平緩的,也就是說,每一年春運時節,12306網站的訪問量會出現瞬時突增的現象。

再好比,小米秒殺系統,在上午10點開售商品,10點前的訪問量比較平緩,10點時一樣會出現併發量瞬時突增的現象。

因此,秒殺系統的流量和併發量咱們能夠使用下圖來表示。

005

由圖能夠看出,秒殺系統的併發量存在瞬時凸峯的特色,也叫作流量突刺現象。

咱們能夠將秒殺系統的特色總結以下。

006

(1)限時、限量、限價

在規定的時間內進行;秒殺活動中商品的數量有限;商品的價格會遠遠低於原來的價格,也就是說,在秒殺活動中,商品會以遠遠低於原來的價格出售。

例如,秒殺活動的時間僅限於某天上午10點到10點半,商品數量只有10萬件,售完爲止,並且商品的價格很是低,例如:1元購等業務場景。

限時、限量和限價能夠單獨存在,也能夠組合存在。

(2)活動預熱

須要提早配置活動;活動還未開始時,用戶能夠查看活動的相關信息;秒殺活動開始前,對活動進行大力宣傳。

(3)持續時間短

購買的人數數量龐大;商品會迅速售完。

在系統流量呈現上,就會出現一個突刺現象,此時的併發訪問量是很是高的,大部分秒殺場景下,商品會在極短的時間內售完。

秒殺系統的技術特色

咱們能夠將秒殺系統的技術特色總結以下。

008

(1)瞬時併發量很是高

大量用戶會在同一時間搶購商品;瞬間併發峯值很是高。

(2)讀多寫少

系統中商品頁的訪問量巨大;商品的可購買數量很是少;庫存的查詢訪問數量遠遠大於商品的購買數量。

在商品頁中每每會加入一些限流措施,例如早期的秒殺系統商品頁會加入驗證碼來平滑前端對系統的訪問流量,近期的秒殺系統商品詳情頁會在用戶打開頁面時,提示用戶登陸系統。這都是對系統的訪問進行限流的一些措施。

(3)流程簡單

秒殺系統的業務流程通常比較簡單;整體上來講,秒殺系統的業務流程能夠歸納爲:下單減庫存。

針對這種短期內大流量的系統來講,就不太適合使用系統擴容了,由於即便系統擴容了,也就是在很短的時間內會使用到擴容後的系統,大部分時間內,系統無需擴容便可正常訪問。 那麼,咱們能夠採起哪些方案來提高系統的秒殺性能呢?

秒殺系統方案

針對秒殺系統的特色,咱們能夠採起以下的措施來提高系統的性能。

007

(1)異步解耦

將總體流程進行拆解,核心流程經過隊列方式進行控制。

(2)限流防刷

控制網站總體流量,提升請求的門檻,避免系統資源耗盡。

(3)資源控制

將總體流程中的資源調度進行控制,揚長避短。

因爲應用層可以承載的併發量比緩存的併發量少不少。因此,在高併發系統中,咱們能夠直接使用OpenResty由負載均衡層訪問緩存,避免了調用應用層的性能損耗。你們能夠到https://openresty.org/cn/來了解有關OpenResty更多的知識。同時,因爲秒殺系統中,商品數量比較少,咱們也能夠使用動態渲染技術,CDN技術來加速網站的訪問性能。

若是在秒殺活動開始時,併發量過高時,咱們能夠將用戶的請求放入隊列中進行處理,併爲用戶彈出排隊頁面。

009

秒殺系統時序圖

網上不少的秒殺系統和對秒殺系統的解決方案,並非真正的秒殺系統,他們採用的只是同步處理請求的方案,一旦併發量真的上來了,他們所謂的秒殺系統的性能會急劇降低。咱們先來看一下秒殺系統在同步下單時的時序圖。

同步下單流程

010

1.用戶發起秒殺請求

在同步下單流程中,首先,用戶發起秒殺請求。商城服務須要依次執行以下流程來處理秒殺請求的業務。

(1)識別驗證碼是否正確

商城服務判斷用戶發起秒殺請求時提交的驗證碼是否正確。

(2)判斷活動是否已經結束

驗證當前秒殺活動是否已經結束。

(3)驗證訪問請求是否處於黑名單

在電商領域中,存在着不少的惡意競爭,也就是說,其餘商家可能會經過不正當手段來惡意請求秒殺系統,佔用系統大量的帶寬和其餘系統資源。此時,就須要使用風控系統等實現黑名單機制。爲了簡單,也能夠使用攔截器統計訪問頻次實現黑名單機制。

(4)驗證真實庫存是否足夠

系統須要驗證商品的真實庫存是否足夠,是否可以支持本次秒殺活動的商品庫存量。

(5)扣減緩存中的庫存

在秒殺業務中,每每會將商品庫存等信息存放在緩存中,此時,還須要驗證秒殺活動使用的商品庫存是否足夠,而且須要扣減秒殺活動的商品庫存數量。

(6)計算秒殺的價格

因爲在秒殺活動中,商品的秒殺價格和商品的真實價格存在差別,因此,須要計算商品的秒殺價格。

注意:若是在秒殺場景中,系統涉及的業務更加複雜的話,會涉及更多的業務操做,這裏,我只是列舉出一些常見的業務操做。

2.提交訂單

(1)訂單入口

將用戶提交的訂單信息保存到數據庫中。

(2)扣減真實庫存

訂單入庫後,須要在商品的真實庫存中將本次成功下單的商品數量扣除。

若是咱們使用上述流程開發了一個秒殺系統,當用戶發起秒殺請求時,因爲系統每一個業務流程都是串行執行的,總體上系統的性能不會過高,當併發量過高時,咱們會爲用戶彈出下面的排隊頁面,來提示用戶進行等待。

009

此時的排隊時間多是15秒,也多是30秒,甚至是更長時間。這就存在一個問題:在用戶發起秒殺請求到服務器返回結果的這段時間內,客戶端和服務器之間的鏈接不會被釋放,這就會佔大量佔用服務器的資源。

網上不少介紹如何實現秒殺系統的文章都是採用的這種方式,那麼,這種方式能作秒殺系統嗎?答案是能夠作,可是這種方式支撐的併發量並非過高。此時,有些網友可能會問:咱們公司就是這樣作的秒殺系統啊!上線後一直在用,沒啥問題啊!我想說的是:使用同步下單方式確實能夠作秒殺系統,可是同步下單的性能不會過高。之因此大家公司採用同步下單的方式作秒殺系統沒出現大的問題,那是由於大家的秒殺系統的併發量沒達到必定的量級,也就是說,大家的秒殺系統的併發量其實並不高。

因此,不少所謂的秒殺系統,存在着秒殺的業務,可是稱不上真正的秒殺系統,緣由就在於他們使用的是同步的下單流程,限制了系統的併發流量。之因此上線後沒出現太大的問題,是由於系統的併發量不高,不足以壓死整個系統。

若是1230六、淘寶、天貓、京東、小米等大型商城的秒殺系統是這麼玩的話,那麼,他們的系統早晚會被玩死,他們的系統工程師不被開除纔怪!因此,在秒殺系統中,這種同步處理下單的業務流程的方案是不可取的。

以上就是同步下單的整個流程操做,若是下單流程更加複雜的話,就會涉及到更多的業務操做。

異步下單流程

既然同步下單流程的秒殺系統稱不上真正的秒殺系統,那咱們就須要採用異步的下單流程了。異步的下單流程不會限制系統的高併發流量。

011

1.用戶發起秒殺請求

用戶發起秒殺請求後,商城服務會通過以下業務流程。

(1)檢測驗證碼是否正確

用戶發起秒殺請求時,會將驗證碼一同發送過來,系統會檢驗驗證碼是否有效,而且是否正確。

(2)是否限流

系統會對用戶的請求進行是否限流的判斷,這裏,咱們能夠經過判斷消息隊列的長度來進行判斷。由於咱們將用戶的請求放在了消息隊列中,消息隊列中堆積的是用戶的請求,咱們能夠根據當前消息隊列中存在的待處理的請求數量來判斷是否須要對用戶的請求進行限流處理。

例如,在秒殺活動中,咱們出售1000件商品,此時在消息隊列中存在1000個請求,若是後續仍然有用戶發起秒殺請求,則後續的請求咱們能夠再也不處理,直接向用戶返回商品已售完的提示。

012

因此,使用限流後,咱們能夠更快的處理用戶的請求和釋放鏈接的資源。

(3)發送MQ

用戶的秒殺請求經過前面的驗證後,咱們就能夠將用戶的請求參數等信息發送到MQ中進行異步處理,同時,向用戶響應結果信息。在商城服務中,會有專門的異步任務處理模塊來消費消息隊列中的請求,並處理後續的異步流程。

在用戶發起秒殺請求時,異步下單流程比同步下單流程處理的業務操做更少,它將後續的操做經過MQ發送給異步處理模塊進行處理,並迅速向用戶返回響應結果,釋放請求鏈接。

2.異步處理

咱們能夠將下單流程的以下操做進行異步處理。

(1)判斷活動是否已經結束

(2)判斷本次請求是否處於系統黑名單,爲了防止電商領域同行的惡意競爭能夠爲系統增長黑名單機制,將惡意的請求放入系統的黑名單中。能夠使用攔截器統計訪問頻次來實現。

(3)扣減緩存中的秒殺商品的庫存數量。

(4)生成秒殺Token,這個Token是綁定當前用戶和當前秒殺活動的,只有生成了秒殺Token的請求才有資格進行秒殺活動。

這裏咱們引入了異步處理機制,在異步處理中,系統使用多少資源,分配多少線程來處理相應的任務,是能夠進行控制的。

3.短輪詢查詢秒殺結果

這裏,能夠採起客戶端短輪詢查詢是否得到秒殺資格的方案。例如,客戶端能夠每隔3秒鐘輪詢請求服務器,查詢是否得到秒殺資格,這裏,咱們在服務器的處理就是判斷當前用戶是否存在秒殺Token,若是服務器爲當前用戶生成了秒殺Token,則當前用戶存在秒殺資格。不然繼續輪詢查詢,直到超時或者服務器返回商品已售完或者無秒殺資格等信息爲止。

採用短輪詢查詢秒殺結果時,在頁面上咱們一樣能夠提示用戶排隊處理中,可是此時客戶端會每隔幾秒輪詢服務器查詢秒殺資格的狀態,相比於同步下單流程來講,無需長時間佔用請求鏈接。

此時,可能會有網友會問:採用短輪詢查詢的方式,會不會存在直到超時也查詢不到是否具備秒殺資格的狀態呢?答案是:有可能! 這裏咱們試想一下秒殺的真實場景,商家參加秒殺活動本質上不是爲了賺錢,而是提高商品的銷量和商家的知名度,吸引更多的用戶來買本身的商品。因此,咱們沒必要保證用戶可以100%的查詢到是否具備秒殺資格的狀態。

4.秒殺結算

(1)驗證下單Token

客戶端提交秒殺結算時,會將秒殺Token一同提交到服務器,商城服務會驗證當前的秒殺Token是否有效。

(2)加入秒殺購物車

商城服務在驗證秒殺Token合法並有效後,會將用戶秒殺的商品添加到秒殺購物車。

5.提交訂單

(1)訂單入庫

將用戶提交的訂單信息保存到數據庫中。

(2)刪除Token

秒殺商品訂單入庫成功後,刪除秒殺Token。

這裏你們能夠思考一個問題:咱們爲何只在異步下單流程的粉色部分採用異步處理,而沒有在其餘部分採起異步削峯和填谷的措施呢?

這是由於在異步下單流程的設計中,不管是在產品設計上仍是在接口設計上,咱們在用戶發起秒殺請求階段對用戶的請求進行了限流操做,能夠說,系統的限流操做是很是前置的。在用戶發起秒殺請求時進行了限流,系統的高峯流量已經被平滑解決了,再日後走,其實系統的併發量和系統流量並非很是高了。

因此,網上不少的文章和帖子中在介紹秒殺系統時,說是在下單時使用異步削峯來進行一些限流操做,那都是在扯淡! 由於下單操做在整個秒殺系統的流程中屬於比較靠後的操做了,限流操做必定要前置處理,在秒殺業務後面的流程中作限流操做是沒啥卵用的。

高併發「黑科技」與致勝奇招

假設,在秒殺系統中咱們使用Redis實現緩存,假設Redis的讀寫併發量在5萬左右。咱們的商城秒殺業務須要支持的併發量在100萬左右。若是這100萬的併發所有打入Redis中,Redis極可能就會掛掉,那麼,咱們如何解決這個問題呢?接下來,咱們就一塊兒來探討這個問題。

在高併發的秒殺系統中,若是採用Redis緩存數據,則Redis緩存的併發處理能力是關鍵,由於不少的前綴操做都須要訪問Redis。而異步削峯只是基本的操做,關鍵仍是要保證Redis的併發處理能力。

解決這個問題的關鍵思想就是:分而治之,將商品庫存分開放。

暗度陳倉

咱們在Redis中存儲秒殺商品的庫存數量時,能夠將秒殺商品的庫存進行「分割」存儲來提高Redis的讀寫併發量。

例如,原來的秒殺商品的id爲10001,庫存爲1000件,在Redis中的存儲爲(10001, 1000),咱們將原有的庫存分割爲5份,則每份的庫存爲200件,此時,咱們在Redia中存儲的信息爲(10001_0, 200),(10001_1, 200),(10001_2, 200),(10001_3, 200),(10001_4, 200)。

013

此時,咱們將庫存進行分割後,每一個分割後的庫存使用商品id加上一個數字標識來存儲,這樣,在對存儲商品庫存的每一個Key進行Hash運算時,得出的Hash結果是不一樣的,這就說明,存儲商品庫存的Key有很大機率不在Redis的同一個槽位中,這就可以提高Redis處理請求的性能和併發量。

分割庫存後,咱們還須要在Redis中存儲一份商品id和分割庫存後的Key的映射關係,此時映射關係的Key爲商品的id,也就是10001,Value爲分割庫存後存儲庫存信息的Key,也就是10001_0,10001_1,10001_2,10001_3,10001_4。在Redis中咱們能夠使用List來存儲這些值。

在真正處理庫存信息時,咱們能夠先從Redis中查詢出秒殺商品對應的分割庫存後的全部Key,同時使用AtomicLong來記錄當前的請求數量,使用請求數量對從Redia中查詢出的秒殺商品對應的分割庫存後的全部Key的長度進行求模運算,得出的結果爲0,1,2,3,4。再在前面拼接上商品id就能夠得出真正的庫存緩存的Key。此時,就能夠根據這個Key直接到Redis中獲取相應的庫存信息。

移花接木

在高併發業務場景中,咱們能夠直接使用Lua腳本庫(OpenResty)從負載均衡層直接訪問緩存。

這裏,咱們思考一個場景:若是在秒殺業務場景中,秒殺的商品被瞬間搶購一空。此時,用戶再發起秒殺請求時,若是系統由負載均衡層請求應用層的各個服務,再由應用層的各個服務訪問緩存和數據庫,其實,本質上已經沒有任何意義了,由於商品已經賣完了,再經過系統的應用層進行層層校驗已經沒有太多意義了!!而應用層的併發訪問量是以百爲單位的,這又在必定程度上會下降系統的併發度。

爲了解決這個問題,此時,咱們能夠在系統的負載均衡層取出用戶發送請求時攜帶的用戶id,商品id和秒殺活動id等信息,直接經過Lua腳本等技術來訪問緩存中的庫存信息。若是秒殺商品的庫存小於或者等於0,則直接返回用戶商品已售完的提示信息,而不用再通過應用層的層層校驗了。 針對這個架構,咱們能夠參見本文中的電商系統的架構圖(正文開始的第一張圖)。

寫在最後

若是以爲文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公衆號,跟冰河學習高併發編程技術。

最後,附上併發編程須要掌握的核心技能知識圖,祝你們在學習併發編程時,少走彎路。

sandahexin_20200322

 
分類:  高併發

 

---------------------------------------------------------------------------------------------------

 

 

《吊打面試官》系列 - 秒殺系統設計 - 極客分享你知道的越多,你不知道的越多 點贊再看,養成習慣 GitHub 上已經開源 https://github.com/JavaFamily 有一線大廠面試點腦圖和我的聯繫方式,歡迎 Star 和指教 絮叨 以前寫......

你知道的越多,你不知道的越多

點贊再看,養成習慣

GitHub 上已經開源 https://github.com/JavaFamily 有一線大廠面試點腦圖和我的聯繫方式,歡迎 Star 和指教

絮叨

以前寫了不少 Redis 相關的知識點,我又大概回頭看了下,除了比較底層的東西沒寫很深以外,我基本上的點都提到過了,我相信若是隻是爲了應付面試應該是夠了的,可是若是你想把它們真正的吸取納爲己用,仍是須要大量的知識積累,和不少實際操做的。

就我本身而言 Redis 在開發過程當中實在用得太廣泛了,熱點數據的存儲啊,總體性能的提高啊都會用到,可是就像我說的技術就是一把雙刃劍,使用它們隨之而來的問題也會不少的,我在老東家雙十二就遇到緩存雪崩問題讓總體服務宕機 3 分鐘,相必你們都知道阿里今年的雙十一數據了,那三分鐘在這種時候到底值多少錢?真的不敢想象。

Redis 的廣泛我就拿掘金我本身的認知舉例,不知道對不對,可是目測是對的。

 

 

你們看到問題所在了麼?是的熱門的讚的數據不是最新的,我盲猜一波上面的熱門文章是緩存。失效時間應該是幾十分鐘的,爲啥這麼作呢?

熱門文章是你們共同都會看到的,也就是熱點數據,在那作緩存,他是不須要那麼高的實時性的,那下面的文章列表是最新發布的文章,有高實時性的特色,你們訪問多的放在緩存還能夠給 DB 減小壓力,我也不知道掘金是否是這麼作的哈,反正道理是這麼個道理了。

那什麼場景是使用 Redis 比較複雜的場景,並且須要大量中間件和業務邏輯去配合的呢?

秒殺!是的就是今天的主題秒殺,我就用我本身的思路帶你們一塊兒看一下,設計一個秒殺從前到後,從內到外到底要技術人員作多少準備。

正文

首先設計一個系統以前,咱們須要先確認咱們的業務場景是怎麼樣子的,我就帶着你們一塊兒假設一個場景好吧。

場景

咱們現場要賣 100 件下面這個嬰兒紙尿褲,而後咱們根據以往這樣秒殺活動的數據經驗來看,目測來搶這 100 件紙尿褲的人足足有 10 萬人。(南極人打錢!)

 

 

你一聽,完了呀,這咱們的服務器哪裏頂得住啊!說真的直接打 DB 確定掛。可是別急嘛,有暖男敖丙在,咱們在開始以前應該先思考下會出現哪些問題?

問題

高併發:

是的高併發這個是咱們想都不用想的一個點,一瞬間這麼多人進來這不是高併發何時是呢?

是吧,秒殺的特色就是這樣時間極短、 瞬間用戶量大。

正常的店鋪營銷都是用極低的價格配合上短信、APP 的精準推送,吸引特別多的用戶來參與這場秒殺,爽了商家苦了開發呀。

秒殺你們都知道若是真的營銷到位,價格誘人,幾十萬的流量我以爲徹底不是問題,那單機的 Redis 我感受 3-4W 的 QPS 仍是能頂得住的,可是再高了就沒辦法了,那這個數據隨便搞個熱銷商品的秒殺可能都不止了。

大量的請求進來,咱們須要考慮的點就不少了,緩存雪崩,緩存擊穿,緩存穿透這些我以前提到的點都是有可能發生的,出現問題打掛 DB 那就很難受了,活動失敗用戶體驗差,活動人氣沒了,最後背鍋的仍是開發。

 

 

超賣:

但凡是個秒殺,都怕超賣,我這裏舉例的只是尿不溼,要是換成 100 個華爲 MatePro30,商家的預算經費賣 100 個能夠賺點還能夠造勢,結果你寫錯程序多賣出去 200 個,你不發貨用戶投訴你,平臺封你店,你發貨就血虧,你怎麼辦?
(沒事看了敖丙的文章直接不怕)

那最後只能殺個開發祭天解氣了,秒殺的價格原本就低了,基本上都是不怎麼賺錢的,超賣了就恐怖了呀,因此超賣也是很關鍵的一個點。

 

 

惡意請求:

你這麼低的價格,假如我搶到了,我轉手賣掉我不是血賺?就算我不賣我也不虧啊,那用戶知道,你知道,別的別有用心的人(黑客、黃牛…)確定也知道的。

那簡單啊,我知道你何時搶,我搞個幾十臺機器搞點腳本,我也模擬出來十幾萬我的左右的請求,那我是否是意味着我基本上有 80% 的成功率了。

真實狀況可能遠遠不止,由於機器請求的速度比人的手速每每快太多了,在貴州的敖丙我每一年回家搶高鐵票都是秒光的,我也不知道有沒有黃牛的功勞,我要 Diss 你,黃牛。杰倫演唱會門票搶不到,我也 Diss 你。

Tip:科普下,小道消息瞭解到的,黃牛的搶票系統,比國內不少小公司的系統還吊不少,架構設計都是頂級的,我用頂配的服務加上頂配的架構設計,你還想看演唱會?還想回家?

不過不用黃牛我回家都難,咱們雲貴川跟我同樣要回家過年的仔太多了 555!

連接暴露:

前面幾個問題你們可能都很好理解,一看到這個有的小夥伴可能會比較疑惑,啥是連接暴露呀?

 

 

相信是個開發同窗都對這個畫面一點都不陌生吧,懂點行的仔均可以打開谷歌的開發者模式,而後看看你的網頁代碼,有的就有 URL,可是我寫 VUE 的時候是事件觸發而後去調用文件裏面的接口看源碼看不到,可是我能夠點擊一下查看你的請求地址啊,不過你好像能夠對按鈕在秒殺前置灰。

無論怎麼樣子都有危險,撇開外面的全部的東西你都擋住了,你賣這個東西實在便宜得過度,有誘惑力,你能保證開發不動心?開發知道地址,在秒殺的時候本身提早請求。。。(開發:怎麼 TM 又是我)

 

 

數據庫:

每秒上萬甚至十幾萬的 QPS(每秒請求數)直接打到數據庫,基本上都要把庫打掛掉,並且你服務不僅僅是作秒殺的還涉及其餘的業務,你沒作降級、限流、熔斷啥的,別的一塊兒掛,小公司的話可能全站崩潰 404。

反正無論你秒殺怎麼掛,你別把別的搞掛了對吧,搞掛了就不是殺一個程序員能搞定的。

程序員:我 TM 好難啊!

問題都列出來了,那怎麼設計,怎麼解決這些問題就是接下去要考慮的了,咱們對症下藥。

服務單一職責:

設計個能抗住高併發的系統,我以爲仍是得單一職責。

什麼意思呢,你們都知道如今設計都是微服務的設計思想,而後再用分佈式的部署方式

也就是咱們下單是有個訂單服務,用戶登陸管理等有個用戶服務等等,那爲啥咱們不給秒殺也開個服務,咱們把秒殺的代碼業務邏輯放一塊兒

單獨給他創建一個數據庫,如今的互聯網架構部署都是分庫的,同樣的就是訂單服務對應訂單庫,秒殺咱們也給他創建本身的秒殺庫

至於表就看你們怎麼設計了,該設置索引的地方仍是要設置索引的,建完後記得用 explain 看看 SQL 的執行計劃。(不瞭解的小夥伴也沒事,MySQL 章節我會說的)

單一職責的好處就是就算秒殺沒抗住,秒殺庫崩了,服務掛了,也不會影響到其餘的服務。(強行高可用)

秒殺連接加鹽:

咱們上面說了連接要是提早暴露出去可能有人直接訪問 url 就提早秒殺了,那又有小夥伴要說了我作個時間的校驗就行了呀,那我告訴你,知道連接的地址比起頁面人工點擊的仍是有很大優點。

我知道 url 了,那我經過程序不斷獲取最新的北京時間,能夠達到毫秒級別的,我就在 00 毫秒的時候請求,我敢說絕對比你人工點的成功率大太多了,並且我能夠一毫秒發送 N 次請求,搞很差你賣 100 個產品我全拿了。

 

 

那這種狀況怎麼避免?

簡單,把 URL 動態化,就連寫代碼的人都不知道,你就經過 MD5 之類的加密算法加密隨機的字符串去作 url,而後經過前端代碼獲取 url 後臺校驗才能經過。

暖男我呢,又準備了一個簡單的 url 加密給你們嚐嚐鮮,還不點個贊?

 

 

Redis 集羣:

以前不是說單機的 Redis 頂不住嘛,那簡單多找幾個兄弟啊,秒殺原本就是讀多寫少,那大家是否是瞬間想起來我以前跟大家提到過的,Redis 集羣,主從同步、讀寫分離,咱們還搞點哨兵,開啓持久化直接無敵高可用!

 

 

Nginx:

Nginx 你們想必都不陌生了吧,這玩意是高性能的 web 服務器,併發也隨便頂幾萬不是夢,可是咱們的 Tomcat 只能頂幾百的併發呀,那簡單呀負載均衡嘛,一臺服務幾百,那就多搞點,在秒殺的時候多租點流量機。

Tip:據我所知國內某大廠就是在去年春節活動期間租光了亞洲全部的服務器,小公司也很喜歡在雙十一期間買流量機來頂住壓力。

 

 

這樣一對比是否是以爲你的集羣能頂不少了。

惡意請求攔截也須要用到它,通常單個用戶請求次數太誇張,不像人爲的請求在網關那一層就得攔截掉了,否則請求多了他搶不搶獲得是一回事,服務器壓力上去了,可能佔用網絡帶寬或者把服務器打崩、緩存擊穿等等。

資源靜態化:

秒殺通常都是特定的商品還有頁面模板,如今通常都是先後端分離的,因此頁面通常都是不會通過後端的,可是前端也要本身的服務器啊,那就把能提早放入 cdn 服務器的東西都放進去,反正把全部能提高效率的步驟都作一下,減小真正秒殺時候服務器的壓力

按鈕控制:

你們有沒有發現沒到秒殺前,通常按鈕都是置灰的,只有時間到了,才能點擊。

這是由於怕你們在時間快到的最後幾秒秒瘋狂請求服務器,而後還沒到秒殺的時候基本上服務器就掛了。

這個時候就須要前端的配合,定時去請求你的後端服務器,獲取最新的北京時間,到時間點再給按鈕可用狀態。

按鈕能夠點擊以後也得給他置灰幾秒,否則他同樣在開始以後一直點的。你敢說大家秒殺的時候不是這樣的?

 

 

限流:

限流這裏我以爲應該分爲前端限流和後端限流。

前端限流:這個很簡單,通常秒殺不會讓你一直點的,通常都是點擊一下或者兩下而後幾秒以後才能夠繼續點擊,這也是保護服務器的一種手段。

後端限流:秒殺的時候確定是涉及到後續的訂單生成和支付等操做,可是都只是成功的幸運兒纔會走到那一步,那一旦 100 個產品賣光了,return 了一個 false,前端直接秒殺結束,而後你後端也關閉後續無效請求的介入了。

Tip:真正的限流還會有限流組件的加入例如:阿里的 Sentinel、Hystrix 等。我這裏就不展開了,就說一下物理的限流。

庫存預熱:

秒殺的本質,就是對庫存的搶奪,每一個秒殺的用戶來你都去數據庫查詢庫存校驗庫存,而後扣減庫存,撇開性能因素,你不以爲這樣好繁瑣,對業務開發人員都不友好,並且數據庫頂不住啊。

開發:你 tm 總算爲我着想一次了。

 

 

那怎麼辦?

咱們都知道數據庫頂不住可是他的兄弟非關係型的數據庫 Redis 能頂啊!

那不簡單了,咱們要開始秒殺前你經過定時任務或者運維同窗提早把商品的庫存加載到 Redis 中去,讓整個流程都在 Redis 裏面去作,而後等秒殺結束了,再異步的去修改庫存就行了。

可是用了 Redis 就有一個問題了,咱們上面說了咱們採用主從,就是咱們會去讀取庫存而後再判斷而後有庫存纔去減庫存,正常狀況沒問題,可是高併發的狀況問題就很大了。

這裏我就不畫圖了,我原本想畫圖的,想了半天我以爲語言可能更好表達一點。

多品幾遍!!!就好比如今庫存只剩下 1 個了,咱們高併發嘛,4 個服務器一塊兒查詢了發現都是還有 1 個,那你們都以爲是本身搶到了,就都去扣庫存,那結果就變成了 - 3,是的只有一個是真的搶到了,別的都是超賣的。咋辦?

Lua:

以前的文章就簡單的提到了他,我今天就多必定點篇幅說一下吧。

Lua 腳本功能是 Reids 在 2.6 版本的最大亮點, 經過內嵌對 Lua 環境的支持, Redis 解決了長久以來不能高效地處理 CAS (check-and-set)命令的缺點, 而且能夠經過組合使用多個命令, 輕鬆實現之前很難實現或者不能高效實現的模式。

Lua 腳本是相似 Redis 事務,有必定的原子性,不會被其餘命令插隊,能夠完成一些 Redis 事務性的操做。這點是關鍵。

知道原理了,咱們就寫一個腳本把判斷庫存扣減庫存的操做都寫在一個腳本丟給 Redis 去作,那到 0 了後面的都 Return False 了是吧,一個失敗了你修改一個開關,直接擋住全部的請求,而後再作後面的事情嘛。

限流 & 降級 & 熔斷 & 隔離:

這個爲啥要作呢,不怕一萬就怕萬一,萬一你真的頂不住了,限流,頂不住就擋一部分出去可是不能說不行,降級,降級了仍是被打掛了,熔斷,至少不要影響別的系統,隔離,你自己就獨立的,可是你會調用其餘的系統嘛,你快不行了你別拖累兄弟們啊。

 

 

削峯填谷:

一說到這個名詞,不少小夥伴就知道了,對的 MQ,你買東西少了你直接 100 個請求改庫我以爲沒問題,可是萬一秒殺一萬個,10 萬個呢?服務器掛了,程序員又要背鍋的。

Tip:可能小夥伴說咱們業務達不到這個量級,不必。可是我想說咱們寫代碼,就不該該寫出有邏輯漏洞的代碼,至少之後公司體量上去了,別人一看竟然不用改代碼,一看代碼做者是敖丙?有點東西!

你能夠把它放消息隊列,而後一點點消費去改庫存就行了嘛,不過單個商品其實一次修改就夠了,我這裏說的是某個點多個商品一塊兒秒殺的場景,像極了雙十一零點。

總結

到這裏我想我已經基本上把該考慮的點還有對應的解決方案也都說了一下,不知道還有沒有沒考慮到的,可是就算沒考慮到我想我這個設計,應該也能撐住一個完整的秒殺流程。

(有大佬的話給敖丙點多的思路,去 GitHub https://github.com/JavaFamily 上給我提,也有個人聯繫)

最後我就畫個完整的流程圖給你們收個尾吧!

 

 


Tip:這個鏈路仍是比較簡單的,不少細節的點所有畫出來就太複雜了,我上面已經提到了全部的注意點了,你們都看看,真正的秒殺有比我這個簡單的,也有比我這個複雜 N 倍的,以前的電商老東家就作的很高級,有機會也能夠跟大家探討,不過是面試嘛,我就給思路,讓你理解比較關鍵的點。

秒殺這章我腦細胞死了不少,考慮了不少個點,最後仍是出來了,忍不住給本身點贊!

(這章是真的不要白嫖,每次都看了不點贊,大家想白嫖我麼?大家好壞喲,不過我好喜歡)

總結

咱們玩歸玩,鬧歸鬧,別拿面試開玩笑。

秒殺不必定是每一個同窗都會問到的,至少確定沒 Redis 基礎那樣常問,可是一旦問到,你們必定要回答到點上。

至少你得說出可能出現的狀況,須要注意的狀況,以及對於的解決思路和方案。

最後就是須要對整個鏈路比較熟悉,注意是一個完整的鏈路,前端怎麼設計的呀,網關的做用呀,怎麼解決 Redis 的併發競爭啊,數據的同步方式呀,MQ 的做用啊。

(提到 MQ 又是一整條的知識鏈路,什麼異步、削峯、解耦等等,因此面試,咱們仍是不打沒有把握的勝仗)

流着淚說再見

Redis 系列到此是真的要跟你們說再見了,寫了 7 篇文章,其實不少大佬的思路和片斷真心贊,其實你們看出來了個人文章我的風格色彩特別濃厚,我我的在生活中就是這麼說話的,也但願用這種風格把本來枯燥乏味的知識點讓你們都像看小說同樣津津有味的看下去,不知道你們什麼感覺,好的很差的都請給我留言。

我這個系列的我會寫到我 GitHub https://github.com/JavaFamily 圖中全部的知識點,之後就麻煩你們多多關照了,我寫做的時間都是業餘時間,基本上週末和晚上的時間都貢獻出來了,我也是個新人不少點也沒接觸到,也要看書看資料才能寫出來,因此有時候仍是但願你們多多包涵。

那咱們下期見!

下期寫____?

不告訴你,哈哈!

平常求贊

好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是人才。

我後面會每週都更新幾篇《吊打面試官》系列和互聯網經常使用技術棧相關的文章,很是感謝人才們能看到這裏,若是這個文章寫得還不錯,以爲「敖丙」我有點東西的話 求點贊👍 求關注❤️ 求分享👥 對暖男我來講真的 很是有用!!!

創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見!

敖丙 | 文 【原創】【轉載請聯繫本人】 若是本篇博客有任何錯誤,請批評指教,不勝感激 !

《吊打面試官》系列每週持續更新,能夠關注個人公衆號「JavaFamily」第一時間閱讀和催更(公衆號比博客早一到兩篇喲),本文 GitHub 上已經收錄 https://github.com/JavaFamily,有一線大廠面試點思惟導圖,歡迎 Star 和完善,裏面也有我我的聯繫方式有什麼問題也能夠直接找我,也有人才交流羣,咱們一塊兒有點東西。

 

 

---------------------------------------------------------------------------------------------------

 

秒殺系統架構解密與防刷設計

呂毅 程序員日誌 2015-08-24
 

↑↑↑

當你決定關注「日誌君」,你已然超越了99%的程序員

 

日誌君導讀:

 

此文是根據呂毅在【QCON高可用架構羣】中的分享內容整理而成, 主要介紹搶購業務形態的業務架構設計。

 

轉自:InfoQ高可用架構公衆號,做者:呂毅。點擊原文閱讀查看網頁版文章。

 

 

呂毅,百度資深研發工程師,LAMP人。
2012年重新浪加入百度移動服務事業羣。在百度期間,隨着產品線發展和業務上QPS增加,架構設計方面略有所獲,對移動端業務、優化有獨特的理解和方法。

 

搶購業務介紹


搶購、閃購,從國外風靡後,國內各種網站都開始作類似的業務,咱們耳熟能詳的惟品會、淘寶、京東都有這類業務。搶購,更多出如今電商網站。那麼,今天和你們一塊兒學習下搶購業務形態的業務架構設計。

咱們常見的搶購業務分兩種: 限時搶購、限量搶購,我簡單分了下這些case,以下圖:



 

想必小米的搶購運營的最火爆了,每發一款新品,都限量發售,每次搞的你們內心癢癢的。記得以前還由於搶購太火爆,站點打不開,崩潰了。那麼問題來了:爲何搶購老是引起RD、OP恐慌?我理解是,爆品太火爆,瞬時請求太大,致使業務機器、存儲機器都在搶購高峯時扛了太多壓力。那麼,咱們今天以一個搶購業務場景爲例,看看如何扛住壓力,作好搶購業務!假設,這時候咱們接到了產品層面的需求,以下圖:

 

 

PM也挺呵呵的,又要有時段的要求、又要有限量的要求,大而全吶!
不過,對於我們RD同窗,也不是問題,咱們一塊兒來看看,如何設計業務架構,把需求知足的棒棒噠!


首先,咱們冷靜的看看需求。

 

需求說:商品數據來自資源方。(哦,咱們沒有商品數據。)
需求說:天天要有好幾場搶購,每場搶購都有商品限制(哦,有點商場促銷還限量甩的feel)
需求說:商品要基於用戶位置排序(哦,移動端業務嘛,這種需求老是有的)
需求說:balabala……

 

具體搶購項目中的設計


經過咱們先行的搶購需求分析,咱們畫一個粗略的流程圖,以下:



咱們將自身簡單劃爲兩部分:業務層、數據層,而且旁路設計一個「運營控制」環節。


固然,數據源自第三方嘛,咱們的數據層基於第三方資源數據構建。
這時,咱們來看看這個草圖裏幾個庫和幾個數據流,是怎樣的。

 

首先,看看庫。數據層的「商品庫」,顯而易見,用於存儲第三方商品數據,經過第三方推、咱們拉的方式來構建這個數據庫信息。數據庫層的「搶購計劃」庫,主要由旁路的「運營控制」環節產生的數據,由運營同窗來維護搶購場次、商品數量。

 

業務層的「搶購庫」,實際上是商品庫的子集,由運營同窗勾選商品並配好該商品放出多少用於搶購,發佈到業務層面的搶購庫中。

業務層的Transaction Data,一會咱們講到與第三方對帳時候,咱們再說它。

 

如何解耦先後端壓力


咱們此時回顧下目錄,目錄中咱們講,如何隔離先後端壓力呢?作法是:

  1. 讓咱們業務的壓力,不會傳遞到資源方,避免形成資源方接口壓力同比增加。因此,咱們本身建了商品庫,此時,第三方笑了。

  2. 業務層與數據層解耦,咱們讓搶購庫位於業務層,讓商品庫位於數據層。由於咱們能夠想象到,搶購高峯來臨時,查詢「商品還有沒有?」的請求是最多的,若「有沒有」這種高頻請求每次都去數據層,那咱們其實就將業務、數據耦合在一塊兒了,那麼,就有了搶購庫這個子庫,在業務層抗壓力。(這裏能夠明確的是,數據層的商品庫爲關係型存儲,業務層的搶購庫爲nosql的)

     

有了業務層的nosql(咱們就用redis吧)抗高頻壓力,數據層的商品庫笑了。


這裏就能夠拋一個思想了:咱們的架構設計中,須要分解壓力,在互聯網項目中,來自於用戶的大流量很多見,這些流量最終都會落到一個地方,就看咱們的設計如何分解這個壓力了,如何避免它層層傳遞。拋個case,咱們的水平分佈業務機器,也是考慮經過水平擴展實例的方式,來分解大流量壓力。


不扯概念的東西了,咱們迴歸咱們的搶購業務。


有了簡單的分層設計,解決的你們都擔憂的壓力問題,咱們就看看搶購業務的時序是怎樣的。


咱們的時序圖分兩個視角來講明:

  1. 商品的角度;

  2. 用戶的角度;

 

商品角度的時序圖,從左到右:資源方、數據層、旁路-運營控制層、業務層。以下圖:

 




錄入商品 即商品從資源方發佈到咱們的數據層,形式能夠是經過API、能夠是經過文件傳輸、能夠是咱們去拉去。經過咱們的代碼邏輯,記錄到咱們數據層的「商品庫DB」。


有了自建的商品庫的數據,咱們的運營同窗就能夠基於商品庫設計天天的搶購場次(此事就有Web界面的事情,這裏咱們就不展開這塊了),運營同窗建立好一批搶購場次,記錄在數據層的「搶購計劃」這一關係型數據庫中。


運營同窗建立完搶購場次後,沒完事,還得應產品需求,基於商品庫,配置每場搶購場次中覆蓋的商品,及商品的數量。這些搶購場次內的商品配置,會簡單的記錄在業務層的「搶購庫」中。(搶購庫記錄的信息較爲簡單,例如商品庫中ID爲123123的商品有100件,業務層的搶購庫中只存ID 123123商品運營配了在第X場搶購中有5件)
此時,數據層的 商品庫有了資源方數據、數據層的 搶購計劃庫中有運營配的搶購計劃,業務層的搶購庫中每場搶購活動中商品的狀況。


那麼,業務層此時就能夠基於時間,來展現運營配的搶購場次了。業務層,如何展現,這塊就是拼裝數據、前端效果了,這裏也不展開了。
假設此事某場場次的搶購活動已經開始,咱們再看看用戶角度的時序圖:

 

 

用戶點擊某個商品的搶購按鈕,業務層代碼首先去看看搶購計劃庫此時是否開始(此步可緩存、也可cache在前端頁面或Client,如有cache的話,此步可忽略)。若搶購在進行中,此時業務代碼須要查詢商品在本次搶購中的庫存還有否(高頻請求,即圖中「爭取名額」階段)。
「爭搶名額」這塊,一會咱們細講,先把時序圖說完。


若用戶搶到了名額,就容許用戶跳轉到第三方的支付頁面產生消費。(此時第三方笑了),產生消費後,第三方本身的庫存-1,而且能夠實時、異步、完事對帳的方式通知咱們。

 

如何保證商品庫的庫存可靠


 

此時,咱們回顧下目錄,「如何保證商品庫的庫存可靠」。
咱們實際上是將商品庫的子庫前置在業務層抗壓力。那麼,如何保證你們的庫存狀況穩定,不會由於搶購業務,致使庫存波動影響用戶體驗。這裏就須要提一個業務RD須要關注的問題,須要作好取捨。要麼,咱們保證你們看到的庫存規律一致,要麼,咱們保證單個用戶看到的庫存規律一致。若保證你們看到的庫存減小的規律一致,且同一時刻庫存你們看到的庫存都同樣。這就對系統有數據強一致性要求,須要很大成本,還只能逐漸逼近此要求要求的效果。而咱們若選擇後者,僅保證單個用戶看到的庫存減小規律一致,雖放棄了數據強一致,但以更少的時間儘量實現了最好的效果。

 

因此,咱們用到了用戶來排隊,若搶到名額了,在搶購庫中的庫存 —(減減),這樣單用戶操做期間,能看到規律的減小,不會出現此事看剩10個,一會看還有11個的狀況。這時咱們說如何內部排隊,如何來控制「查詢商品在本次搶購中的庫存還有否(高頻請求)」這個高頻請求

 

咱們構建商品維度的cache,上圖中雖說是「隊列」,咱們能夠用redis的list來真正實現個隊列,也能夠經過 /—來實現。

 

假設商品A,運營配了20件,此事來了N多用戶的請求,業務代碼都會來查詢cache_prefix_a_id這個隊列的長度,若隊列長度≤0,則有權去—(減減)搶購庫的商品庫存。若隊列長度在20件內,則經過業務代碼內的等待來等待隊頭的位置,而後得到搶購權限。若隊列長度太長,則能夠直接返回,認爲商品已被搶空。

 

這時插入一個運營配庫的時序,便於你們理解。該時序圖有詳細的說明和標註,就不展開了,以下圖:

 

此時,咱們能夠想象,若上游用戶的請求壓力是N,這個N會壓在業務層的搶購庫,俗話說「責任止於此」。

 

如何和第三方多方對帳


那麼,咱們回顧目錄「如何和第三方多方對帳」?
這裏就要提到「Transaction Data」這個庫了。


Transaction ID爲用戶維度的Session記錄,用戶從進入搶購業務開始,產生一個Transaction ID,該Transaction ID生命週期截止到用戶跳轉去第三方支付爲止。期間在生活服務中產生的瀏覽、搶購行爲均會掛靠到該Transaction ID之下,並會在跳轉去第三方支付頁時攜帶該Transaction ID憑證。最主要的是須要記錄下:用戶得到商品名額後,跳轉去第三方時,這一行爲。


考慮到Transaction ID爲搶購業務中,用戶操做行爲的關鍵字段,值須要保證惟一。故此處能夠採用發號器之類的能力。


咱們構建的Transaction Data記錄,就能夠按照DailyRun的方式,與第三方對帳,來fix兩方數據庫庫存不一致等問題。


爲何會產生咱們和資源方的庫存不一致,多是由於用戶在第三方消費後,第三方callback咱們時候失敗形成,也多是由於用戶跳去第三方後並無真正支付,但咱們的商品庫、搶購庫的庫存都已經減小形成的。緣由可能有不少,對帳機制是必要的。

 

項目總結


最後,咱們回顧回顧設計,壓力問題在業務層解決了,庫存不一致問題咱們經過對帳機制解決了,產品的需求咱們也經過旁路可配解決了,嗯,能夠喝杯茶,發起評審,評審經過後開始寫代碼了。 :)

感謝你們。分享中的數據強一致那塊,以及如何作取捨,都是頗有意思的點,均可以展開聊好久,這裏沒展開,你們能夠過後查查資料。

 

Q & A


Q1:防刷是怎麼作的?通常搶購都有很大優惠。若是有人惡意刷,那正常的用戶就失去了購買的機會。好比,搶購的商品數爲1000,有人惡意刷了900,那只有 100 被正經常使用戶搶到。等惡意搶到的 900 通過後面的支付環節驗證後,可能已通過了搶購時間了。就算惡意搶到的 900 都支付成功,那對正經常使用戶也是不公平的。

 

在這個業務場景中,咱們作的是商品展現、商品的購買權的發放,真正產生消費是在第三方。那麼,用戶刷的問題,須要咱們和第三方支付頁面一塊兒來控制。在用戶經過排隊機制,得到了購買名額後,跳轉去第三方時候,咱們按照和第三方約定的加密方式傳遞加密信息,第三方按照約定的解密方式解密成功後才容許用戶支付,加密解密的過程當中能夠帶具備生命週期的內容。這樣,用戶在高頻請求支付頁面獲取商品時候,實際只有:1)加密對;2)第一次,纔可能得到。不過,第三方都是爲了銷售出商品,因此這類合做的成功概率不大。惡意刷,的確會在咱們的業務層面展現商品沒量了。致使想買的用戶沒了機會,但能夠保證第三方不受損。這種刷的狀況,若想在咱們業務層規避,我想這就是一個通用的防SPAM的問題了。這塊本身真懂得很少。

Q2:要想準確的放刷,判斷的維度就多,邏輯就複雜;與之矛盾的,搶購要求的是響應迅速。

 

對的,搶購業務由於請求壓力大、熱門商品搶購併發高,切忌增長過多邏輯,切忌過多後端依賴,越簡單效果越好。咱們在設計系統時候,不少事不是我們一個系統能cover的,多少須要一些前置模塊、能力的構建ready後,咱們的系統才能run的不錯。建議構建賬號體系、用戶消費記錄這兩部分。

Q3:對帳只是和第三方去對比商品的庫存量嗎,金額是否去對比?

對帳,實際上是對比的消費數據。避免出現咱們統計今日產生了X件商品共價值Y的消費,第三方給出的是消費了N件共M價值的消費。避免金額不一致,形成結算、分紅等問題的出現。我想你問題中的庫存量的diff問題,還得靠第三方按期的經過咱們數據層的接口來update他們提供的商品。其實在咱們的商品庫中,商品不必定只容許第三方提供,也能夠容許第三方經過接口減小商品嘛,好比和一個賣水果的第三方合做,第三方上週發佈說有100件,但這周線下熱銷,只剩20件了,咱們也應該容許第三方來update到一個低值。但這樣,咱們的系統中就會複雜挺多。

Q4:防刷,避免第三方的推廣效果達不到問題。

 

對的,用戶ID維度、IP維度,都是有效辦法。看具體場景。有賬號體系的業務,用用戶ID維度效果最好,藉助存儲記錄下每一個用戶的購買記錄,來控制就好。市面上的電商網站,基本是搶購業務都須要登陸,而且限制每件商品單人購買數量,其實就是經過存儲記錄用戶的消費,而且再次產生消費前查詢並增長代碼邏輯來控制。

Q5:每次搶購活動的時候用一套新的驗證碼?

 

驗證碼這個東東,屬於圖靈測試嘛,只要測試方法好,而且儘量保證每次產生的驗證信息從未出現過且無規律,就是好的驗證碼啦。

 

---------------------------------------------------------------------------------------------------

 


王富平/3.4 Lambda 架構與推薦在電商網站實踐.257
3.4.1 Lambda 架構257
3.4.2 1 號店推薦系統實踐260
3.4.3 Lambda 的將來262
3.4.4 思考263
3.4.5 疑問與解惑263

---------------------------------------------------------------------------------------------------

Lambda 架構與推薦在電商網站實踐

2015-11-24 16:36| 發佈者: 煉數成金_小數 | 查看: 14636| 評論: 0| 原做者: 王富平 | 來自: 高可用架構

 
摘要: 高可用架構分享及傳播在架構領域具備典型意義的文章,本文根據王富平分享記錄。轉載請註明高可用架構公衆號 ArchNotes。王富平現爲 1 號店搜索與精準化部門架構師,以前在百度從事數據挖掘相關工做,對實時處理有着深入 ...
 
 
王富平 
現爲 1 號店搜索與精準化部門架構師,以前在百度從事數據挖掘相關工做, 對實時處理有着深入的研究。一直從事大數據相關研發工做, 2013 年開發了一款 SQL 實時處理框架,致力於建設高可用的大數據業務系統。
 
1、Lambda 架構
Lambda 架構由 Storm 的做者 Nathan Marz 提出。 旨在設計出一個能知足實時大數據系統關鍵特性的架構,具備高容錯、低延時和可擴展等特性。
 
Lambda 架構整合離線計算和實時計算,融合不可變性(Immutability),讀寫分離和複雜性隔離等一系列架構原則,可集成 Hadoop,Kafka,Storm,Spark,HBase 等各種大數據組件。
 
1.1 Lambda 架構理論點
 
Lambda 架構對系統作了以下抽象:
 
Query = Function(All Data)
 
簡言之:查詢是應用於數據集的函數。 data 是自變量,query 是因變量。
 
Lambda 有兩個假設
不可變假設:Lambda 架構要求 data 不可變,這個假設在大數據系統是廣泛成立的:由於日誌是不可變的,某個時刻某個用戶的行爲,一旦記錄下來就不可變。
 
Monoid 假設: 理想狀況下知足 Monoid 的 function 能夠轉換爲:
 
query = function(all data/ 2) + function(all data/ 2)
 
Monoid 的概念來源於範疇學(Category Theory),其一個重要特性是知足結合律。如整數的加法就知足 Monoid 特性:(a+b)+c=a+(b+c)
 
不知足 Monoid 特性的函數不少時候能夠轉化成多個知足 Monoid 特性的函數的運算。如多個數的平均值 avg 函數, 多個平均值無法直接經過結合來獲得最終的平均值,可是能夠拆成分母除以分子,分母和分子都是整數的加法,從而知足 Monoid 特性
 
1.2 Lambda 架構
 
三層架構:批處理層、實時處理層、服務層,如圖 1 所示:

 

 

圖 1
 
批處理層:批量處理數據,生成離線結果
實時處理層:實時處理在線數據,生成增量結果
服務層:結合離線、在線計算結果,推送上層
 
1.3 Lambda 架構優缺點
 
優勢:
實時:低延遲處理數據
可重計算:因爲數據不可變,從新計算同樣能夠獲得正確的結果
容錯:第二點帶來的,程序 bug、系統問題等,能夠從新計算
複雜性分離、讀寫分離
 
缺點:
開發和運維的複雜性:Lambda 須要將全部的算法實現兩次,一次是爲批處理系統,另外一次是爲實時系統,還要求查詢獲得的是兩個系統結果的合併,可參考 http://www.infoq.com/cn/news/2014/09/lambda-architecture-questions
 
1.4 典型推薦架構
 
實時處理範式的需求
推薦系統的最終目的是提升轉化率,手段是推送用戶感興趣的、須要的產品。爲何須要實時處理範式?
 
1 號店會根據你實時瀏覽、加車、收藏、從購物車刪除、下單等行爲,計算相關產品的權重,把相應的產品馬上更新到猜你喜歡欄位。一樣在亞馬遜搜索瀏覽了《基督山伯爵》這本書,亞馬遜首頁很快增長一行新推薦:包含 4 個版本《基督山伯爵》
 
答案不言而喻:讓推薦引擎更具時效性。如圖 二、圖 3 所示:

 

 

圖 2

 

 

圖 3
 
Netflix 推薦架構
 
Netflix 推薦架構如圖 4 所示

 

 

圖 4
 
批處理層:從 Hive、pig 數據倉庫,離線計算推薦模型,生成離線推薦結果
實時處理層:從消息隊列(Hermes、User Event Queue)實時拉取用戶行爲數據與事件,生成在線推薦結果
服務層:結合離線、在線推薦結果,爲用戶生成推薦列表
 
2、1 號店推薦系統實踐
 
2.1. 推薦引擎組件
目前共有 6 大推薦引擎:
 
用戶意圖:實時分析用戶行爲,存儲短時間內興趣偏好
用戶畫像:用戶興趣偏好的長期積累(商品類目、品牌等),天然屬性(年齡、性別),社會屬性(居住地、公司)
千人千面:羣體分析(某一大學、某一小區、公司、好友羣)
情境推薦:根據季節、節日、天氣等特定情境作推薦
反向推薦:根據商品購買週期等,方向生成推薦結果
主題推薦:分析用戶與主題的匹配度(如:美食家、極客等),根據主題對用戶進行推薦
 
產品架構如圖 5 所示

 

 

圖 5
 
今天主要討論其中的主題推薦
 
2.2 主題推薦
 
首先主題推薦有三個步驟
 
創建關係(主題與商品,用戶與商品,用戶與主題)
選品,創建主題選品池
推薦,根據用戶與主題的關係,從選品池爲用戶進行推薦 用公式表示就是:Topic_recommend = topic_recommend_function(offline data) 僅僅完成上面步驟,不須要 「實時處理範式」 就能夠完成 後來主題推薦加入了 「增量推薦」 功能,經過用戶的實時行爲,對推薦結果進行調整
 
根據用戶在線行爲(瀏覽、購買、評論)等,調整離線推送的主題推薦結果 用公式表示就是 Topic_recommend= mege (topic_recommend_function1(offline data), topic_recommend_function2(online data) )
 
顯然這演變成了一個 Lambda 架構,如圖 6 所示

 

 

圖 6
 
2.3 主題推薦存儲設計
 
存儲最重要的就是 「主題推薦結果表」,須要知足以下特性
 
KV 查詢,根據用戶 id 查詢推薦結果;
保留必定時間內歷史推薦數據。
根據上述兩個特色,咱們決定選用 HBase。HBase 的 kv、多版本屬性知足上述需求。有以下兩個要點
 
讀寫分離
咱們使用 HBase 主從方式,來讀寫分離,採用 HBase 主從的主要緣由是
 
在 CAP 理論裏面 HBase 犧牲的是可用性保證強一致性,flush、split、compaction 都會影響可用性。檢測 region server 掛斷、恢復 region 都須要必定時間,這段時間內 region 數據不可用。
離線任務大量讀寫,對 region server 形成壓力(gc、網絡、flush、compaction),影響前端響應速度。
 
Cache
爲了進一步提升響應速度,咱們在服務層增長了一級緩存,採用 1 號店內部分佈式緩存 ycache(與 memcache 的封裝)。
 
產品效果如圖 7 所示

 

 

圖 7
 
2.4 HBase 的維護
 
熱點均衡:不要期望預 split 解決一切問題,熱點的形成不可避免,尤爲隨着業務數據的增加, 一些冷 region 該合併就合併
作好爲 HBase 修復 bug 的準備,尤爲是升級新版本。
3、Lambda 的將來
與其說 Lambda 的將來,不如說 「實時處理範式」 與「批處理範式」的將來。工程實踐中 Lambda 以前提到的缺點有很多體會
 
邏輯一致性。許多公共數據分析邏輯須要實現兩套,而且須要保證一致性。換個角度來看就是公共邏輯提取費力。
 
維護、調試兩套平臺
Jay Kreps 認爲 Lambda 架構是大數據方案中的臨時解決方案,緣由是目前工具不成熟。 他提供了一個替代架構,該架構基於他在 Linkedin 構建 Kafka 和 Samza 的經驗,他還聲稱該架構在具備相同性能特性的同時還具備更好的開發和運維特性。

 

 

圖 8
 
讓我想起了 Spark streaming 既能夠作實時處理,又很天然作批量。讓我想起了 Storm 的 DRPC,就是爲了作離線處理。有人說 streaming 本質是批量方式,實際上 「實時」 沒有絕對界限,關鍵在於延遲。你認爲 10s,我也能夠認爲 2s 內纔算實時。
 
對於 Lambda 架構問題,社區提出了 Kappa 架構,一套系統知足實時、批處理需求。 目前看來,是朝着 「實時」框架去主動包含 「批量處理」 的方向發展
 
4、我的的兩點思考
兩種不一樣的需求,一個框架搞定,是否是很熟悉?咱們都想搞大而全,一勞永逸的事情,但許多每每被證實是錯的。
MR 是否是過期了?we need more,期待着數據與邏輯更便捷、更深刻的交集。
 
5、Q&A
Q1:HBase 大家遇到最詭異的是啥問題?
由於 hdfs 客戶端沒有設置讀超時,致使 HBase lock hang 住,最後集羣宕機。
 
Q2:玩推薦引擎首先想到的是 mahout,王老師是否也有這方面的涉獵?
mahout、mlib 這些東西都是數據挖掘框架,主要看算法好壞,選誰區別不大。
 
Q3:日誌量多大?Kafka 集羣配置怎樣 broker、replica 等?碰到什麼坑嗎?
1 天 2T 多數據,Kafka 是整個公司公用。Kafka 仍是比較穩定,咱們這邊幾乎沒遇到問題,Storm 問題出了很多。Kafka 集羣 replica 有些是 二、有些 3,broker 是 10。遇到大量數據的時候 Kafka 每隔一陣可能出現 CLOSE_WAIT 的問題
 
Q4:千人千面引擎最後體現的效果是什麼?用在什麼地方?
千人千面效果,針對小區用戶轉化率提高 100%
 
Q5:請問下推薦排序時使用了什麼算法,以及大概多少人負責算法模塊?
在 app 首頁正在嘗試邏輯迴歸和 learn to rank,7~8 人作算法
 
Q6:1 號店對新登錄用戶作什麼推薦處理? 主題推薦人工介入量有多大?1 號店對其推薦算法出過轉化率外,從算法角度會關心哪些指標?
新用戶冷啓動,採用兩個策略
數據平滑
熱銷優質商品補充
推薦最重要的是看排序效果,主要是推薦位置的轉換率。
 
Q7:Storm 都遇到哪些填很久都填不完的坑能夠分享下麼?
Storm 在高 tps 時候容易消息堆積。以前讀 Kafka,拉的模式。實時推薦須要實時的反應用戶的行爲,用戶明明下單了還在推薦。後來讀取訂單的行爲用了自主研發的 jumper,推的方式解決了快速獲得訂單行爲,其餘行爲用 Kafka。
資源分配、隔離不合理。其餘任務出現內存泄露等問題會影響其餘任務 task。
Q8:HBase 熱點問題怎麼解決的呢?是分析 key 的分佈,而後寫腳本 split 麼?
基本思路同樣,寫工具檢測。重點在 request 量,不在 key 的分佈。
 
Q9:批處理層向服務層推送離線計算結果的週期是怎樣的?會因數據量大而對線上的 HBase 形成衝擊嗎?
目前是一天一次,衝擊不大。 一、錯峯; 二、bulkload;三、讀寫分離。

---------------------------------------------------------------------------------------------------

 

 


楊 碩/3.5 某公司線上真實流量壓測工具構建.265
3.5.1 爲何要開發一個通用的壓測工具265
3.5.2 常見的壓測工具.266
3.5.3 構建本身的壓測工具266
3.5.4 疑問與解惑271

---------------------------------------------------------------------------------------------------

(1 條消息) 美團線上真實流量壓測工具構建_weixin_45583158 的博客 - CSDN 博客

美團線上真實流量壓測工具構建

編者按:高可用架構分享及傳播在架構領域具備典型意義的文章,本文由楊碩在高可用架構羣分享。點擊上方藍字訂閱高可用架構可獲取進羣機會。

楊碩,現就任於美團,負責廣告運營平臺、美團點評廣告系統對接等工做。曾就任於 Yahoo ! 北研,從事廣告產品的研發工做。對於架構設計、性能調優、大數據、前端等領域均有所涉獵。業餘時間除了 side project 外,還常常參加 Hackathon 比賽,並得到了美團第四屆 Hackathon 優勝獎等獎項。

「目前美團壓測項目接入的服務 40+,打壓的次數 1000+,很好的完成了線上業務的打壓需求。」  —— 楊碩

 

爲何要搞一個通用的壓測工具

美團內部的 RPC 服務大多構建在 Thrift 之上,在平常開發服務的過程當中,須要針對這些服務進行壓力測試(如下簡稱壓測)來發現潛在問題。經常使用的方法有:

  1. 使用一些腳本語言如:Python、Ruby 等,讀取線上日誌構建請求,用多線程模擬用戶請求進行壓測;

  2. 使用開源工具進行壓測。

然而,不管採起哪一種方法,壓測都是一個十分耗時而又繁瑣的過程,主要痛點有:

  1. 須要寫不少代碼解析日誌,還原請求,對於比較複雜的請求,解析很容易出錯;

  2. 須要搭建腳本或者工具的運行環境,一般這一過程比較耗時;

  3. 因爲打壓方法沒有統一,致使打壓的結果指標比較混亂,有的結果甚至以終端輸出的方式展現,很是不直觀。

一般要讓一個同窗壓測某一服務,不少時候須要耗費 2~3 天時間,而且不少同窗的表情都是這樣的:

 

 

爲了解決這個問題提供一個簡單好用的壓測工具是十分有必要的。 有沒有必要本身造輪子呢?若是有現成的解決方案,那再好不過了。

常見的壓測工具

JMeter

JMeter 是一個比較老牌的壓測工具,主要針對 HTTP 服務進行打壓,該工具在如下方面並不知足美團內部的壓測需求: 

  1. 默認不支持 Thrift 的打壓測試;

  2. 須要本地安裝,而且配置複雜; 

  3. 對於用戶操做並不友好。 

 


 

Twitter/iago

Twitter/iago 是一個由 Twitter 開源的壓測工具,支持對 HTTP、Thrift 等服務進行壓測,其主要問題以下: 

  1. 對每一個壓測應用都須要建立一個項目;

  2. 壓測結果並不直觀;

  3. 流量重放依賴本地文件;

  4. 項目依賴於一個較老版本的 Scala,搭建不便;

  5. 相關文檔比較少 。

除此以外,當時還考察了 Gatling、Grinder、Locust 等一些常見的壓測工具,都由於適用場景和美團的需求有些出入而排除了。綜上,針對當前壓測工具的一些現狀,構建一個簡單易用的壓測工具仍是頗有必要的。

構建本身的壓測工具

針對以前提到的痛點,新的壓測工具主要提供如下功能:

  1. 簡單易用的操做界面(服務接入壓測的時間應該控制在 1 小時之內);

  2. 清晰的圖表能反映壓測服務的各項指標;

  3. 知足包括 Thrift、HTTP 等服務的壓測需求。

其實不少複雜問題在實用性和靈活性權衡事後,都能用一個簡單模型表示,壓測問題也同樣,咱們將壓測過程抽象,能夠用這個圖表示:

 

 

首先,在 init 方法裏面,進行一些初始化的工做,好比鏈接數據庫,建立客戶端等。

其次,在 run 方法裏面發出壓測請求,爲了保證可以對服務產生足夠的壓力,這裏一般採用多線程併發訪問,同時記錄每次請求的發起時間和結束時間,這兩個時間的簡單相減就可以獲得每次請求的響應時間,利用該結果就能夠計算出 TP90、平均響應時間、最大響應時間等指標。

最後,等壓測結束後,經過 destroy 方法進行資源回收等工做,好比關閉 RPC 服務的鏈接,關閉數據庫等。

若是用接口能夠這樣表示:

 

 

不管是 HTTP 仍是 Thrift 服務的壓測,本質都是 run 方法的不一樣。

有了基本模型後,咱們要解決的另一個痛點是,如何簡單地拷貝線上真實流量用來構建打壓請求,一些大型的 Thrift 服務數據結構很是複雜,寫打壓腳本的時候須要不少代碼來解析日誌,並且容易出錯。

爲了不這種狀況,咱們提供了一個叫 VCR(錄像機)的工具來拷貝流量。VCR 可以將線上的請求序列化後寫到 Redis 裏面。考慮到用戶須要查看具體請求和易用性等需求,最終選取了 JSON 格式做爲序列化和反序列化的協議。同時須要部署在生產環境的一臺機器上,爲了下降對線上服務的影響,這裏採起了單線程異步寫的方式來拷貝流量,以下圖:

 

 

解決了流量拷貝問題後,咱們接下來須要按照咱們採集的指標進行數據聚合運算。常見的指標有:最大響應時間,平均響應時間,QPS,TP90,TP50,這些指標能夠評估服務的性能。

這裏咱們採用了 InfluxDB 來完成數據的聚合工做,全部聚合指標都是一行 SQL 搞定,很是快速。以 TP90 爲例子,僅須要一行查詢就能實現需求。

SELECT PERCENTILE(response_time, 90) FROM test_series GROUP BY time(10s)

綜上,壓測工具的流程圖以下:

 

 

這是第一期的實現,大概花了 1 周左右的業餘時間,等項目上線後,應用的壓測接入時間獲得了明顯的縮短。下圖是每一個步驟的平均耗時:

 

 

細心的同窗可能已經發現了,VCR 是用虛線表示的,緣由是對於新上線的服務,不須要拷貝流量,只須要在實現 runner 的過程當中,經過代碼構造請求數據便可。項目剛上線時,你們還不熟悉,第一次應用接入須要耗時 20 到 40min,等你們有經驗後,下個應用的接入時間會縮短到 15min 左右。對於服務的 owner 來講,該壓測工具更爲靈活,他能夠在 runner 接口中自由實現壓測邏輯。若是二次打壓的話或者修改參數的話,只須要在網頁上點擊鼠標便可完成。下圖是其中某個服務的壓測結果:

 

 

項目上線後,陸陸續續有各事業部的同窗開始使用,你們廣泛反應操做簡單,可是也存在如下幾個問題:

  1. 打壓力度有時不夠,這是因爲一期項目開發時時間比較緊迫,只實現了單機的版本,單臺機器天然不免有些」 馬力不足 " ;

  2. 在接入 Thrift 應用的時候,須要用戶提供 Thrift 生成的客戶端代碼,對於這種方式你們依然以爲繁瑣,但願只上傳一個 Thrift 文件就可以打壓指定服務; 

  3. 用戶但願看到被打壓機器的一些資源利用狀況,好比 CPU 使用率,內存使用率等。

咱們開始針對上述問題進行了解決,針對第一個問題,咱們採用 Akka 來作一個分佈式的擴展,將應用的角色分爲 master、worker、counter。 Master 負責進行任務分發,worker 進行打壓,而後將打壓的的結果發給 counter 進行彙總寫入 InfluxDB。

第二個問題,咱們在簡單模型的基礎上進行更高級別的封裝,用戶上傳 Thrift 文件後,咱們將 Thrift 文件編譯成爲模板代碼後,自動生成對應的接口。同時咱們也有相應的 HTTP 服務適配模塊。

針對最後一個問題,咱們在壓測機器上部署了 agent,壓測期間機器的數據可以經過 agent 源源不斷的提交上來,這樣用戶可以清晰的看到被打壓服務所在機器的性能指標

這是架構升級圖,你們能夠結合我剛剛說的三點看看:

 

 

整個項目的技術棧是 Ratpack + InfluxDB + ActiveJDBC + AngularJS,主力開發語言是 Groovy。

最後說下項目的發展狀況,項目接入的服務 40+,打壓的次數 1000+,很好的完成了同窗們的打壓需求。在作壓測工具的時候最大的感觸是:不少複雜的問題都能用很簡單的模型來解釋,在簡單模型的基礎上按照用戶的需求不斷開發適配才能成爲一個好的框架,Storm,Hadoop 都是如此。項目有些細節的部分你們能夠參考咱們美團的技術 Blog http://t.cn/R4KWHrA。

Q & A

1. 可否對比下 TCPCopy?

TCPCopy 不能作到對請求的靈活控制,並且 TCPCopy 須要單獨在機器上部署,比較麻煩,當時確實是考慮的方案之一

2. 線上流量拷貝是跟線上請求同時的嗎?對線上業務影響有多大?

是和線上請求同時進行,美團的作法是單獨拉一個拷貝流量的分支,修改兩行代碼上線,流量拷貝的話是採起單線程異步的方式,最多采集 1w 條日誌

3. VCR 是在代理服務器上完成流量拷貝仍是跟業務代碼同一個進程?

VCR 和業務代碼是同一個進程,單開異步線程進行拷貝。

4. 爲什麼選擇 InfluxDB 數據庫?有什麼優點嗎?不選其餘的,好比 MongoDB 等等。

選用 InfluxDB 緣由有兩點,第一點是 InfluxDB 的底層是 KV 模型,能夠兼容 RocksDB、Redis 等常見的 KV (最新版本不行了),寫入速度較快。第二點是用 InfluxDB 進行數據聚合運算比較給力,一行 SQL 就能搞定。若是硬要比較的話,Druid 和 influxDB 都能快速實現數據的聚合運算,可是 Druid 主要用在 Big Data 方向。

5. 是否考慮開源?

開源在計劃之中。

6. 業務新功能上線,須要修改壓測代碼嗎?仍是說只要在 UI 裏面修改壓測配置?

若是 RPC 接口不變,不用修改任何代碼,直接 Web 界面上點擊就行。

7. 項目只適合壓測仍是也能夠作自動化測試?

如今主要用來壓測,自動化測試的話若是是界面方面的測試,當前沒有應用場景。若是涉及到具體業務邏輯的測試,則能夠配合壓測一塊兒完成,主要邏輯寫在 run 方法中即可。

8. 有沒有壓測策略配置錯誤,壓錯的時候?

有配置錯誤的時候,這時壓測工具會在 Web 界面上打出出錯信息,若是錯誤的壓測線上服務,一般會有線上報警,通常狀況下很難出現。

9. 新應用不兼容 VCR 如何處理,還有真實流量能夠放大和縮小嗎? 怎麼作?

新應用的話沒有流量拷貝的環節,通常 RD 會本身實現一個構建 Mock 請求的函數,這樣就實現了壓測功能,等項目上線後採集流量,真實流量一旦採集,框架就能夠幫你進行日誌讀取、多倍打壓等功能,流量的放大及縮小取決你但願能有多少 worker 進行打壓,worker 的數量可配。

10.VCR 錄製的序列化的是 httpRequest 對象嗎?

VCR 序列化的是 RPC 請求,主要針對 Thrift。

11. 測試結果能反應整個鏈路的瓶頸在哪嗎?另外測試環境是否是跟線上隔離的?

壓測不能反應整個鏈路的瓶頸,美團內部採用 Falcon 和 Octo 收集依賴服務的指標,若是依賴服務撐不住,會直接報警,目前測試環境都只會壓測線下的服務,不多出現壓線上的狀況,不過機器之間的網絡是打通的。

 

---------------------------------------------------------------------------------------------------

 

 

第4 章 容器與雲計算.273陳 飛/4.1 微博基於Docker 容器的混合雲遷移實戰.2734.1.1 爲何要採用混合雲的架構2734.1.2 跨雲的資源管理與調度.2754.1.3 容器的編排與服務發現.2784.1.4 混合雲監控體系.2844.1.5 前進路上遇到的那些坑.2864.1.6 疑問與解惑286高 磊/4.2 互聯網金融創業公司Docker 實踐2874.2.1 背景介紹2874.2.2 容器選型2874.2.3 應用遷移2884.2.4 彈性擴容2914.2.5 將來規劃2954.2.6 疑問與解惑295高永超/4.3 使用開源Calico 構建Docker 多租戶網絡.2974.3.1 PaaS 平臺的網絡需求.2974.3.2 使用Calico 實現Docker 的跨服務器通信.2984.3.3 利用Profile 實現ACL3014.3.4 性能測試3064.3.5 Calico 的發展3084.3.6 疑問與解惑309彭哲夫/4.4 解析Docker 在芒果TV 的實踐之路3104.4.1 豆瓣時期3104.4.2 芒果TV 的Nebulium Engine .3114.4.3 Project Eru .3124.4.4 細節3134.4.5 網絡3144.4.6 存儲3154.4.7 Scale3164.4.8 資源分配和集羣調度3164.4.9 服務發現和安全.3174.4.10 實例3174.4.11 總結3184.4.12 疑問與解惑318王關勝/4.5 微博基於Docker 的混合雲平臺設計與實踐3234.5.1 微博的業務場景及混合雲背景.3234.5.2 三大基礎設施助力微博混合雲.3264.5.3 微博混合雲DCP 系統設計核心:自動化、彈性調度3284.5.4 引入阿里雲做爲第3 機房,實現彈性調度架構3304.5.5 大規模集羣操做自動化.3314.5.6 不怕峯值事件.332第5 章 運維保障333王 康/5.1 360 如何用QConf 搞定兩萬以上服務器的配置管理.3335.1.1 設計初衷3335.1.2 總體認識3345.1.3 架構介紹3355.1.4 QConf 服務端3365.1.5 QConf 客戶端3365.1.6 QConf 管理端3405.1.7 其餘3415.1.8 疑問與解惑343尤 勇/5.2 深度剖析開源分佈式監控CAT3475.2.1 背景介紹3475.2.2 總體設計3485.2.3 客戶端設計3495.2.4 服務端設計3525.2.5 總結感悟357楊尚剛/5.3 單表60 億記錄等大數據場景的MySQL 優化和運維之道3595.3.1 前言3595.3.2 數據庫開發規範.3605.3.3 數據庫運維規範.3635.3.4 性能優化3685.3.5 疑問與解惑375秦 迪/5.4 微博在大規模、高負載系統問題排查方法3795.4.1 背景3795.4.2 排查方法及線索.3795.4.3 總結3845.4.4 疑問與解惑385秦 迪/5.5 系統運維之爲何每一個團隊存在大量爛代碼3875.5.1 寫爛代碼很容易.3875.5.2 爛代碼終究是爛代碼3885.5.3 重構不是萬能藥.3925.5.4 寫好代碼很難.3935.5.5 悲觀的結語394秦 迪/5.6 系統運維之評價代碼優劣的方法3955.6.1 什麼是好代碼.3955.6.2 結語4035.6.3 參考閱讀403秦 迪/5.7 系統運維之如何應對爛代碼4045.7.1 改善可維護性.4045.7.2 改善性能與健壯性4095.7.3 改善生存環境.4125.7.4 我的感想414第6 章 大數據與數據庫415王 勁/6.1 某音樂公司的大數據實踐.4156.1.1 什麼是大數據.4156.1.2 某音樂公司大數據技術架構4186.1.3 在大數據平臺重構過程當中踩過的坑4256.1.4 後續的持續改進.430王新春/6.2 實時計算在點評.4316.2.1 實時計算在點評的使用場景4316.2.2 實時計算在業界的使用場景4326.2.3 點評如何構建實時計算平臺4336.2.4 Storm 基礎知識簡單介紹.4346.2.5 如何保證業務運行的可靠性4366.2.6 Storm 使用經驗分享4386.2.7 關於計算框架的後續想法4426.2.8 疑問與解惑442王衛華/6.3 百姓網Elasticsearch 2.x 升級之路.4466.3.1 Elasticsearch 2.x 變化4466.3.2 升級之路4486.3.3 優化或建議4516.3.4 百姓之道4526.3.5 後話:Elasticsearch 5.04536.3.6 升級2.x 版本成功,5.x 版本還會遠嗎454董西成 張虔熙/6.4 Hadoop、HBase 年度回顧4576.4.1 Hadoop 2015 技術發展4576.4.2 HBase 2015 年技術發展4606.4.3 疑問與解惑466常 雷/6.5 解密Apache HAWQ——功能強大的SQL-on-Hadoop 引擎.4696.5.1 HAWQ 基本介紹4696.5.2 Apache HAWQ 系統架構.4726.5.3 HAWQ 中短時間規劃.4796.5.4 貢獻到Apache HAWQ 社區4796.5.5 疑問與解惑480蕭少聰/6.6 PostgresSQL HA 高可用架構實戰.4826.6.1 PostgreSQL 背景介紹.4826.6.2 在PostgreSQL 下如何實現數據複製技術的HA 高可用集羣4836.6.3 Corosync+Pacemaker MS 模式介紹4846.6.4 Corosync+Pacemaker M/S 環境配置4856.6.5 Corosync+Pacemaker HA 基礎配置4886.6.5 PostgreSQL Sync 模式當前的問題4926.6.6 疑問與解惑492王晶昱/6.7 從NoSQL 歷史看將來.4956.7.1 前言4956.7.2 1970 年:We have no SQL4966.7.3 1980 年:Know SQL 4976.7.4 2000 年:No SQL .5026.7.5 2005 年:不只僅是SQL 5046.7.6 2013 年:No,SQL .5056.7.7 阿里的技術選擇.5056.7.8 疑問與解惑506楊尚剛/6.8 MySQL 5.7 新特性大全和將來展望.5086.8.1 提升運維效率的特性5086.8.2 優化器Server 層改進.5116.8.3 InnoDB 層優化5136.8.4 將來發展5176.8.5 運維經驗總結.5186.8.6 疑問與解惑519譚 政/6.9 大數據盤點之Spark 篇5216.9.1 Spark 的特性以及功能5216.9.2 Spark 在Hulu 的實踐.5256.9.3 Spark 將來的發展趨勢5286.9.4 參考文章5306.9.5 疑問與解惑530蕭少聰/6.10 從Postgres 95 到PostgreSQL 9.5:新版亮眼特性5326.10.1 Postgres 95 介紹5326.10.2 PostgresSQL 版本發展歷史5336.10.3 PostgresSQL 9.5 的亮眼特性5346.10.4 PostgresSQL 還能夠作什麼5446.10.5 疑問與解惑547畢洪宇/6.11 MongoDB 2015 回顧:全新里程碑式的WiredTiger 存儲引擎5516.11.1 存儲引擎的發展5516.11.2 複製集改進.5556.11.3 自動分片機制5566.11.4 其餘新特性介紹5566.11.5 疑問與解惑.558王曉偉/6.12 基於Xapian 的垂直搜索引擎的構建分析5616.12.1 垂直搜索的應用場景5616.12.2 技術選型.5636.12.3 垂直搜索的引擎架構5646.12.4 垂直搜索技術和業務細節.5666.12.5 疑問與解惑568第7 章 安全與網絡572郭 偉/7.1 揭祕DDoS 防禦——騰訊雲大禹系統5727.1.1 有關DDoS 簡介的問答.5747.1.2 有關大禹系統簡介的問答5757.1.3 有關大禹系統硬件防禦能力的問答5767.1.4 有關算法設計的問答5777.1.5 大禹和其餘產品、技術的區別.578馮 磊 趙星宇/7.2 App 域名劫持之DNS 高可用——開源版HttpDNS 方案詳解5807.2.1 HttpDNSLib 庫組成.5817.2.2 HttpDNS 交互流程5827.2.3 代碼結構5837.2.4 開發過程當中的一些問題及應對.5867.2.5 疑問與解惑593馬 濤/7.3 CDN 對流媒體和應用分發的支持及優化5957.3.1 CDN 系統工做原理.5957.3.2 網絡分發過程當中ISP 的影響6027.3.3 防盜鏈.6037.3.4 內容分發系統的問題和應對思路6047.3.5 P2P 穿牆打洞6077.3.6 疑問與解惑609馬 濤/7.4 HTTPS 環境使用第三方CDN 的證書難題與最佳實踐611蔣海滔/7.5 互聯網主要安全威脅分析及應對方案6137.5.1 互聯網Web 應用面臨的主要威脅6137.5.2 威脅應對方案.6167.5.3 疑問與解惑624