1. 程式人生 > >電商那些年,我摸爬打滾出的高併發架構實戰精髓(續)

電商那些年,我摸爬打滾出的高併發架構實戰精髓(續)

一、分層,分割,分散式

大型網站要很好地支撐高併發,需要長期的規劃設計。在初期,需要把系統進行分層,在發展過程中把核心業務進行拆分成模組單元,根據需求進行分散式部署,可以進行獨立團隊維護開發。

分層:

  • 將系統在橫向維度上切分成幾個部分,每個部門負責一部分相對簡單並比較單一的職責,然後通過上層對下層的依賴和排程組成一個完整的系統。

  • 比如把電商系統分成:應用層,服務層,資料層。(具體分多少個層次根據自己的業務場景)

  • 應用層:網站首頁,使用者中心,商品中心,購物車,紅包業務,活動中心等,負責具體業務和檢視展示。

  • 服務層:訂單服務、使用者管理服務、紅包服務、商品服務等,為應用層提供服務支援。

  • 資料層:關係資料庫、NoSQL資料庫等,提供資料儲存查詢服務。

  • 分層架構是邏輯上的,在物理部署上可以部署在同一臺物理機器上,但是隨著網站業務的發展,必然需要對已經分層的模組分離部署,分別部署在不同的伺服器上,使網站可以支撐更多使用者訪問。

分割:

  • 在縱向方面對業務進行切分,將一塊相對複雜的業務分割成不同的模組單元。

  • 包裝成高內聚低耦合的模組不僅有助於軟體的開發維護,也便於不同模組的分散式部署,提高網站的併發處理能力和功能擴充套件。

  • 比如使用者中心可以分割成:賬戶資訊模組、訂單模組、充值模組、提現模組、優惠券模組等。

分散式:

  • 分散式應用和服務,將分層或者分割後的業務分散式部署,獨立的應用伺服器、資料庫、快取伺服器。

  • 當業務達到一定使用者量時,再進行伺服器均衡負載、資料庫、快取主從叢集。

  • 分散式靜態資源,比如:靜態資源上傳CDN。

  • 分散式計算,比如:使用Hadoop進行大資料的分散式計算。

  • 分散式資料和儲存,比如:各分佈節點根據雜湊演算法或其他演算法分散儲存資料。

(網站分層-來自網路)

二、叢集

對於使用者訪問集中的業務獨立部署伺服器、應用伺服器、資料庫、NoSQL資料庫,核心業務基本上需要搭建叢集,即多臺伺服器部署相同的應用構成一個叢集,通過負載均衡裝置共同對外提供服務, 伺服器叢集能夠為相同的服務提供更多的併發支援,因此當有更多的使用者訪問時,只需要向叢集中加入新的機器即可,另外可以實現當其中的某臺伺服器發生故障時,可通過負載均衡的失效轉移機制將請求轉移至叢集中其他的伺服器上,因而提高系統的可用性。

應用伺服器叢集:

  • Nginx 反向代理

  • SLB

  • … …

(關係/NoSQL)資料庫叢集:

  • 主從分離,從庫叢集

(通過反向代理均衡負載-來自網路)

三、非同步

在高併發業務中如果涉及到資料庫操作,主要壓力都是在資料庫伺服器上面,雖然使用主從分離,但是資料庫操作都是在主庫上操作,單臺數據庫伺服器連線池允許的最大連線數量是有限的 。

當連線數量達到最大值時,其它需要連線資料操作的請求就需要等待有空閒的連線,這樣高併發的時候很多請求就會出現connection time out的情況 。

那麼,像這種高併發業務我們要如何設計開發方案可以降低資料庫伺服器的壓力呢?

如:

  • 自動彈窗簽到,雙11跨0點的時候併發請求籤到介面;

  • 雙11搶紅包活動;

  • 雙11訂單入庫;

  • ……

設計考慮:

  • 逆向思維,壓力在資料庫,那業務介面就不進行資料庫操作不就沒壓力了?

  • 資料持久化是否允許延遲?

  • 如何讓業務介面不直接操作DB,又可以讓資料持久化?

方案設計:

  • 像這種涉及資料庫操作的高併發的業務,就要考慮使用非同步了。

  • 客戶端發起介面請求,服務端快速響應,客戶端展示結果給使用者,資料庫操作通過非同步同步。

  • 如何實現非同步同步?

  • 使用訊息佇列,將入庫的內容enqueue到訊息佇列中,業務介面快速響應給使用者結果(可以溫馨提示高峰期延遲到賬)。

  • 然後再寫個獨立程式從訊息佇列dequeue資料出來進行入庫操作,入庫成功後重新整理使用者相關快取,如果入庫失敗記錄日誌,方便反饋查詢和重新持久化。

  • 這樣一來資料庫操作就只有一個程式(多執行緒)來完成,不會給資料帶來壓力。

補充:

  • 訊息佇列除了可以用在高併發業務,其它只要有相同需求的業務也是可以使用,如:簡訊傳送中介軟體等。

  • 高併發下非同步持久化資料可能會影響使用者的體驗,可以通過可配置的方式,或者自動化監控資源消耗來切換時時或者使用非同步,這樣在正常流量的情況下可以使用時操作資料庫來提高使用者體驗。

  • 非同步同時也可以指程式設計上的非同步函式、非同步執行緒,有的時候可以使用非同步操作,把不需要等待結果的操作放到非同步中,然後繼續後面的操作,節省了等待的這部分操作的時間。

四、快取

高併發業務介面多數都是進行業務資料的查詢,如:商品列表、商品資訊

使用者資訊、紅包資訊等,這些資料都是不會經常變化,並且持久化在資料庫中。

高併發的情況下直接連線從庫做查詢操作,多臺從庫伺服器也抗不住這麼大量的連線請求數(前面說過,單臺數據庫伺服器允許的最大連線數量是有限的),那麼在這種高併發的業務介面要如何設計呢?

設計考慮:

  • 還是逆向思維,壓力在資料庫,那麼我們就不進行資料庫查詢?

  • 資料不經常變化,我們為啥要一直查詢DB?

  • 資料不變化客戶端為啥要向伺服器請求返回一樣的資料?

方案設計:

  • 資料不經常變化,我們可以把資料進行快取,快取的方式有很多種,一般的:應用伺服器直接Cache記憶體,主流的:儲存在memcache、Redis記憶體資料庫。

  • Cache是直接儲存在應用伺服器中,讀取速度快,記憶體資料庫伺服器允許連線數可以支撐到很大,而且資料儲存在記憶體,讀取速度快,再加上主從叢集,可以支撐很大的併發查詢。

  • 根據業務情景,使用配合客戶端本地存,如果我們資料內容不經常變化,為啥要一直請求伺服器獲取相同資料,可以通過匹配資料版本號,如果版本號不一樣介面重新查詢快取返回資料和版本號,如果一樣則不查詢資料直接響應。

  • 這樣不僅可以提高介面響應速度,也可以節約伺服器頻寬,雖然有些伺服器頻寬是按流量計費,但是也不是絕對無限的,在高併發的時候伺服器頻寬也可能導致請求響應慢的問題。

補充:

五、面向服務

  • SOA面向服務架構設計

  • 微服務更細粒度服務化,一系列的獨立的服務共同組成系統

使用服務化思維,將核心業務或者通用的業務功能抽離成服務獨立部署,對外提供介面的方式提供功能。

最理想化的設計是可以把一個複雜的系統抽離成多個服務,共同組成系統的業務,優點:鬆耦合、高可用性、高伸縮性、易維護。

通過面向服務化設計,獨立伺服器部署,均衡負載,資料庫叢集,可以讓服務支撐更高的併發。

服務例子: 使用者行為跟蹤記錄統計

說明:

通過上報應用模組,操作事件,事件物件,等資料,記錄使用者的操作行為。

比如:記錄使用者在某個商品模組,點選了某一件商品,或者瀏覽了某一件商品

背景:

由於服務需要記錄使用者的各種操作行為,並且可以重複上報,準備接入服務的業務又是核心業務的使用者行為跟蹤,所以請求量很大,高峰期會產生大量併發請求。

架構:

  • nodejs WEB應用伺服器均衡負載

  • Redis主從叢集

  • MySQL主

  • nodejs+express+ejs+Redis+MySQL

  • 服務端採用nodejs,nodejs是單程序(PM2根據cpu核數開啟多個工作程序),採用事件驅動機制,適合I/O密集型業務,處理高併發能力強

業務設計:

  • 併發量大,所以不能直接入庫,採用:非同步同步資料,訊息佇列。

  • 請求介面上報資料,介面將上報資料push到redis的list佇列中。

  • nodejs寫入庫指令碼,迴圈pop redis list資料,將資料儲存入庫,並進行相關統計Update,無資料時sleep幾秒。

  • 因為資料量會比較大,上報的資料表按天命名儲存。

介面:

  • 上報資料介面

  • 統計查詢介面

上線跟進:

  • 服務業務基本正常

  • 每天的上報表有上千萬的資料

六、冗餘,自動化

當高併發業務所在的伺服器出現宕機時,需要有備用伺服器進行快速的替代,在應用伺服器壓力大的時候可以快速新增機器到叢集中,所以我們就需要有備用機器可以隨時待命。

最理想的方式是可以通過自動化監控伺服器資源消耗來進行報警,自動切換降級方案,自動地進行伺服器替換和新增操作等,通過自動化可以減少人工的操作的成本,而且可以快速操作,避免人為操作上面的失誤。

冗餘:

  • 資料庫備份

  • 備用伺服器

自動化:

  • 自動化監控

  • 自動化報警

  • 自動化降級

通過GitLab事件,我們應該反思,做了備份資料並不代表就萬無一失了,我們需要保證高可用性,首先備份是否正常進行,備份資料是否可用,需要我們進行定期的檢查,或者自動化監控, 還有包括如何避免人為上的操作失誤問題。(不過事件中Gitlab的開放性姿態,積極的處理方式還是值得學習的)

總結

高併發架構是一個不斷衍變的過程,冰洞三尺非一日之寒,長城築成非一日之功。打好基礎架構方便以後的拓展,這點很重要。