架構師眼裡的高併發架構

Java大蝸牛發表於2018-07-27

前言

高併發經常會發生在有大活躍使用者量,使用者高聚集的業務場景中,如:秒殺活動,定時領取紅包等。

為了讓業務可以流暢的執行並且給使用者一個好的互動體驗,我們需要根據業務場景預估達到的併發量等因素,來設計適合自己業務場景的高併發處理方案。

在電商相關產品開發的這些年,我有幸的遇到了併發下的各種坑,這一路摸爬滾打過來有著不少的血淚史,這裡進行的總結,作為自己的歸檔記錄,同時分享給大家。

伺服器架構

業務從發展的初期到逐漸成熟,伺服器架構也是從相對單一到叢集,再到分散式服務。

一個可以支援高併發的服務少不了好的伺服器架構,需要有均衡負載,資料庫需要主從叢集,nosql快取需要主從叢集,靜態檔案需要上傳cdn,這些都是能讓業務程式流暢執行的強大後盾。

伺服器這塊多是需要運維人員來配合搭建,具體我就不多說了,點到為止。

大致需要用到的伺服器架構如下:

  • 伺服器
  • 均衡負載(如:nginx,阿里雲SLB)
  • 資源監控
  • 分散式
  • 資料庫
  • 主從分離,叢集
  • DBA 表優化,索引優化,等
  • 分散式
  • nosql
  • 主從分離,叢集
  • redis
  • mongodb
  • memcache
  • cdn
  • html
  • css
  • js
  • image

併發測試

高併發相關的業務,需要進行併發的測試,通過大量的資料分析評估出整個架構可以支撐的併發量。

測試高併發可以使用第三方伺服器或者自己測試伺服器,利用測試工具進行併發請求測試,分析測試資料得到可以支撐併發數量的評估,這個可以作為一個預警參考,俗話說知己自彼百戰不殆。

第三方服務:

  • 阿里雲效能測試

併發測試工具:

  • Apache JMeter
  • Visual Studio效能負載測試
  • Microsoft Web Application Stress Tool

實戰方案

通用方案

日使用者流量大,但是比較分散,偶爾會有使用者高聚的情況;

場景: 使用者簽到,使用者中心,使用者訂單,等

伺服器架構圖:

架構師眼裡的高併發架構

說明:

場景中的這些業務基本是使用者進入APP後會操作到的,除了活動日(618,雙11,等),這些業務的使用者量都不會高聚集,同時這些業務相關的表都是大資料表,業務多是查詢操作,所以我們需要減少使用者直接命中DB的查詢;優先查詢快取,如果快取不存在,再進行DB查詢,將查詢結果快取起來。

更新使用者相關快取需要分散式儲存,比如使用使用者ID進行hash分組,把使用者分佈到不同的快取中,這樣一個快取集合的總量不會很大,不會影響查詢效率。

方案如:

  • 使用者簽到獲取積分
  • 計算出使用者分佈的key,redis hash中查詢使用者今日簽到資訊
  • 如果查詢到簽到資訊,返回簽到資訊
  • 如果沒有查詢到,DB查詢今日是否簽到過,如果有簽到過,就把簽到資訊同步redis快取。
  • 如果DB中也沒有查詢到今日的簽到記錄,就進行簽到邏輯,操作DB新增今日簽到記錄,新增簽到積分(這整個DB操作是一個事務)
  • 快取簽到資訊到redis,返回簽到資訊
  • 注意這裡會有併發情況下的邏輯問題,如:一天簽到多次,發放多次積分給使用者。
  • 我的博文[大話程式猿眼裡的高併發]有相關的處理方案。
  • 使用者訂單
  • 這裡我們只快取使用者第一頁的訂單資訊,一頁40條資料,使用者一般也只會看第一頁的訂單資料
  • 使用者訪問訂單列表,如果是第一頁讀快取,如果不是讀DB
  • 計算出使用者分佈的key,redis hash中查詢使用者訂單資訊
  • 如果查詢到使用者訂單資訊,返回訂單資訊
  • 如果不存在就進行DB查詢第一頁的訂單資料,然後快取redis,返回訂單資訊
  • 使用者中心
  • 計算出使用者分佈的key,redis hash中查詢使用者訂單資訊
  • 如果查詢到使用者資訊,返回使用者資訊
  • 如果不存在進行使用者DB查詢,然後快取redis,返回使用者資訊
  • 其他業務
  • 上面例子多是針對使用者儲存快取,如果是公用的快取資料需要注意一些問題,如下
  • 注意公用的快取資料需要考慮併發下的可能會導致大量命中DB查詢,可以使用管理後臺更新快取,或者DB查詢的鎖住操作。
  • 我的博文[大話Redis進階]對更新快取問題和推薦方案的分享。

以上例子是一個相對簡單的高併發架構,併發量不是很高的情況可以很好的支撐,但是隨著業務的壯大,使用者併發量增加,我們的架構也會進行不斷的優化和演變,比如對業務進行服務化,每個服務有自己的併發架構,自己的均衡伺服器,分散式資料庫,nosql主從叢集,如:使用者服務、訂單服務;

訊息佇列

秒殺、秒搶等活動業務,使用者在瞬間湧入產生高併發請求

場景:定時領取紅包,等

伺服器架構圖:

架構師眼裡的高併發架構

說明:

場景中的定時領取是一個高併發的業務,像秒殺活動使用者會在到點的時間湧入,DB瞬間就接受到一記暴擊,hold不住就會當機,然後影響整個業務;

像這種不是隻有查詢的操作並且會有高併發的插入或者更新資料的業務,前面提到的通用方案就無法支撐,併發的時候都是直接命中DB;

設計這塊業務的時候就會使用訊息佇列的,可以將參與使用者的資訊新增到訊息佇列中,然後再寫個多執行緒程式去消耗佇列,給佇列中的使用者發放紅包;

方案如:

  • 定時領取紅包
  • 一般習慣使用 redis的 list
  • 當使用者參與活動,將使用者參與資訊push到佇列中
  • 然後寫個多執行緒程式去pop資料,進行發放紅包的業務
  • 這樣可以支援高併發下的使用者可以正常的參與活動,並且避免資料庫伺服器當機的危險

附加:

通過訊息佇列可以做很多的服務。

如:定時簡訊傳送服務,使用sset(sorted set),傳送時間戳作為排序依據,簡訊資料佇列根據時間升序,然後寫個程式定時迴圈去讀取sset佇列中的第一條,當前時間是否超過傳送時間,如果超過就進行簡訊傳送。

一級快取

高併發請求連線快取伺服器超出伺服器能夠接收的請求連線量,部分使用者出現建立連線超時無法讀取到資料的問題;

因此需要有個方案當高併發時候時候可以減少命中快取伺服器;

這時候就出現了一級快取的方案,一級快取就是使用站點伺服器快取去儲存資料,注意只儲存部分請求量大的資料,並且快取的資料量要控制,不能過分的使用站點伺服器的記憶體而影響了站點應用程式的正常執行,一級快取需要設定秒單位的過期時間,具體時間根據業務場景設定,目的是當有高併發請求的時候可以讓資料的獲取命中到一級快取,而不用連線快取nosql資料伺服器,減少nosql資料伺服器的壓力

比如APP首屏商品資料介面,這些資料是公共的不會針對使用者自定義,而且這些資料不會頻繁的更新,像這種介面的請求量比較大就可以加入一級快取;

伺服器架構圖:

架構師眼裡的高併發架構

合理的規範和使用nosql快取資料庫,根據業務拆分快取資料庫的叢集,這樣基本可以很好支援業務,一級快取畢竟是使用站點伺服器快取所以還是要善用。

靜態化資料

高併發請求資料不變化的情況下如果可以不請求自己的伺服器獲取資料那就可以減少伺服器的資源壓力。

對於更新頻繁度不高,並且資料允許短時間內的延遲,可以通過資料靜態化成JSON,XML,HTML等資料檔案上傳CDN,在拉取資料的時候優先到CDN拉取,如果沒有獲取到資料再從快取,資料庫中獲取,當管理人員操作後臺編輯資料再重新生成靜態檔案上傳同步到CDN,這樣在高併發的時候可以使資料的獲取命中在CDN伺服器上。

CDN節點同步有一定的延遲性,所以找一個靠譜的CDN伺服器商也很重要

針對上面的技術我特意整理了一下,有很多技術不是靠幾句話能講清楚,所以乾脆找朋友錄製了一些視訊,很多問題其實很簡單,但是背後的思考和邏輯不簡單,要做到知其然還要知其所以然。費分享給大家。

其他方案

對於更新頻繁度不高的資料,APP,PC瀏覽器,可以快取資料到本地,然後每次請求介面的時候上傳當前快取資料的版本號,服務端接收到版本號判斷版本號與最新資料版本號是否一致,如果不一樣就進行最新資料的查詢並返回最新資料和最新版本號,如果一樣就返回狀態碼告知資料已經是最新。


相關文章