Java生鮮電商平臺-高可用微服務系統如何設計?

来源:https://www.cnblogs.com/jurendage/archive/2019/11/17/11878692.html
-Advertisement-
Play Games

Java生鮮電商平臺-高可用微服務系統如何設計? 說明:Java生鮮電商平臺高可用架構往往有以下的要求: 高可用。這類的系統往往需要保持一定的 SLA,7*24 時不間斷運行不代表完全不掛,而是有一定的百分比的。 例如我們常說的可用性需達到 4 個 9(99.99%),全年停機總計不能超過 1 小時 ...


Java生鮮電商平臺-高可用微服務系統如何設計?

 

說明:Java生鮮電商平臺高可用架構往往有以下的要求:

高可用。這類的系統往往需要保持一定的 SLA,7*24 時不間斷運行不代表完全不掛,而是有一定的百分比的。

例如我們常說的可用性需達到 4 個 9(99.99%),全年停機總計不能超過 1 小時,約為 53 分鐘,也即服務停用時間小於 53 分鐘,就說明高可用設計合格。

用戶分佈在全國。大規模微服務系統所支撐的用戶一般在全國各地,因而每個地區的人,都希望能夠就近訪問,所以一般不會一套系統服務全國,而是每個地區都要有相應的業務單元,使得用戶可以就近訪問。

併發量大,存在波峰波谷。微服務之所以規模比較大,其實是承載的壓力比較大,而且需要根據請求的波峰波谷進行彈性伸縮。

有故障性能診斷和快速恢復的機制。大規模微服務場景下,運維人員很難進行命令式手動運維來控制應用的生命周期,應該採用聲明式的運維方法。

另外一旦有了性能瓶頸或者故障點,應該有自動發現定位的機制,迅速找到瓶頸點和故障點,及時修複,才能保障 SLA。

戰略設計

為了滿足以上的要求,這個系統絕不是運維組努力一把,或者開發組努力一把,就能解決的,是一個端到端的,各個部門共同完成的一個目標,所以我們常稱為戰略設計。

研發

一個能支撐高併發,高可用的系統,一定是需要從研發環節就開始下功夫的。

首先,每一個微服務都有實現良好的無狀態化處理,冪等服務介面設計

狀態分為分發,處理,存儲幾個過程,如果對於一個用戶的所有的信息都保存在一個進程中,則從分發階段,就必須將這個用戶分發到這個進程,否則無法對這個用戶進行處理。

然而當一個進程壓力很大的時候,根本無法擴容,新啟動的進程根本無法處理那些保存在原來進程的用戶的數據,不能分擔壓力。

所以要將整個架構分成兩個部分,無狀態部分和有狀態部分,而業務邏輯的部分往往作為無狀態的部分,而將狀態保存在有狀態的中間件中,如緩存,資料庫,對象存儲,大數據平臺,消息隊列等。

這樣無狀態的部分可以很容易的橫向擴展,在用戶分發的時候,可以很容易分發到新的進程進行處理,而狀態保存到後端。

而後端的中間件是有狀態的,這些中間件設計之初,就考慮了擴容的時候,狀態的遷移,複製,同步等機制,不用業務層關心。

對於數據的存儲,主要包含幾類數據:

會話數據等,主要保存在記憶體中。對於保存在記憶體里的數據,例如 Session,可以放在外部統一的緩存中。

結構化數據,主要是業務邏輯相關。對於業務相關的數據,則應該保存在統一的資料庫中。

文件圖片數據,比較大,往往通過 CDN 下發。對於文件,照片之類的數據,應該存放在統一的對象存儲裡面。

非結構化數據,例如文本,評論等。對於非結構化數據,可以存在統一的搜索引擎裡面,例如 ElasticSearch。

但是還有一個遺留的問題,就是已經分發,正在處理,但是尚未存儲的數據,肯定會在記憶體中有一些,在進程重啟的時候,數據還是會丟一些的,那這部分數據怎麼辦呢?

這部分就需要通過重試進行解決,當本次調用過程中失敗之後,前序的進程會進行重試,例如 Dubbo 就有重試機制。

既然重試,就需要介面是冪等的,也即同一次交易,調用兩次轉賬 1 元,不能最終轉走 2 元。

介面分為查詢,插入,更新,刪除等操作:

對於查詢介面來講,本身就是冪等的,不用做特殊的判斷。

對於插入介面來講,如果每一個數據都有唯一的主鍵,也能保證插入的唯一性,一旦不唯一,則會報錯。

對於更新操作來講,則比較複雜,分兩種情況。一種情況是同一個介面,前後調用多次的冪等性。另一種情況是同一個介面,併發環境下調用多次的正確性。

 

為了保持冪等性,往往要有一個冪等表,通過傳入冪等參數匹配冪等表中 ID 的方式,保證每個操作只被執行一次,而且在實行最終一致性的時候,可以通過不斷重試,保證最終介面調用的成功。

對於併發條件下,誰先調用,誰後調用,需要通過分散式鎖如 Redis,ZooKeeper 等來實現同一個時刻只有一個請求被執行,如何保證多次執行結果仍然一致呢?則往往需要通過狀態機,每個狀態只流轉一次。

還有就是樂觀鎖,也即分散式的 CAS 操作,將狀態的判斷、更新整合在一條語句中,可以保證狀態流轉的原子性。樂觀鎖並不保證更新一定成功,需要有對應的機制來應對更新失敗。

其次,根據服務重要度實現熔斷降級、限流保護策略

服務拆分多了,在應用層面就會遇到以下問題:

服務雪崩:即一個服務掛了,整個調用鏈路上的所有的服務都會受到影響。

大量請求堆積、故障恢復慢:即一個服務慢,卡住了,整個調用鏈路出現大量超時,要長時間等待慢的服務恢復到正常狀態。

為瞭解決這些問題,我們在應用層面實施了以下方案:

通過熔斷機制,當一個服務掛了,被影響的服務能夠及時熔斷,使用 Fallback 數據保證流程在非關鍵服務不可用的情況下,仍然可以進行。

通過線程池和消息隊列機制實現非同步化,允許服務快速失敗,當一個服務因為過慢而阻塞,被影響服務可以在超時後快速失敗,不會影響整個調用鏈路。

當發現整個系統的確負載過高的時候,可以選擇降級某些功能或某些調用,保證最重要的交易流程的通過,以及最重要的資源全部用於保證最核心的流程。

還有一種手段就是限流,當既設置了熔斷策略,又設置了降級策略,通過全鏈路的壓力測試,應該能夠知道整個系統的支撐能力。

因而就需要制定限流策略,保證系統在測試過的支撐能力範圍內進行服務,超出支撐能力範圍的,可拒絕服務。

當你下單的時候,系統彈出對話框說 “系統忙,請重試”,並不代表系統掛了,而是說明系統是正常工作的,只不過限流策略起到了作用。

其三,每個服務都要設計有效探活介面,以便健康檢查感知到服務狀態

當我們部署一個服務的時候,對於運維部門來講,可以監控機器的狀態或者容器的狀態是否處於啟動狀態,也可以監控到進程是否啟動,埠是否監聽等。

但是對於已經啟動的進程,是否能夠正常服務,運維部門無法感知,需要開發每個服務的時候,設計一個有效探活介面,讓運維的監控系統可以通過調用這個介面,來判斷進程能夠正常提供服務。

這個介面不要直接返回,而是應該在進程內部探查提供服務的線程是否出去正常狀態,再返回相應的狀態編碼。

只有這樣,開發出來的服務和運維才能合作起來,保持服務處於某個副本數,否則如果一部分服務雖然啟動,但是處於假死狀態,會使得其他正常服務,無法承受壓力。

其四,通過制定良好的代碼檢查規範和靜態掃描工具,最大化限制因為代碼問題造成的系統不可用

要保持線上代碼的高可用性,代碼質量是關鍵,大部分線上問題,無論是性能問題,還是穩定性問題,都是代碼造成的,而非基礎設施造成的。

而且基礎設施的可用率為 99.95%,但是服務層要求的可用率高於這個值,所以必須從業務層高可用來彌補。

除了下麵的高可用架構部分,對於每一個服務來講,制定良好的代碼檢查規範和靜態掃描工具,通過大量的測試用例,最大化限制因為代碼問題造成的系統不可用,是必須的,是高可用的基礎。

高可用架構設計

在系統的每一個部分,都要避免單點。系統冗餘往往分管控面和數據面,而且分多個層次,往往每一個層次都需要進行高可用的設計。

   

在機房層面,為了高可用應該部署在多個區域,或者多個雲,每個區域分多個可用區進行部署。

對於雲來講,雲的管控要多機房高可用部署,使得任何一個機房故障,都會使得管控依然可以使用。

這就需要管控的組件分佈於至少兩個機房,管控的資料庫和消息隊列跨機房進行數據同步。

對於雲的數據面來講,入口的網關要和機房網路配合做跨機房的高可用,使得入口公網 IP 和負載均衡器,在一個機房故障的情況下,可以切換至另一個機房。

   

在雲之上要部署 Kubernetes 平臺,管控層面 Kubernetes 要實現高可用部署,etcd 要跨機房高可用部署,Kubernetes 的管控組件也要跨機房部署。

當然還有一種情況是機房之間距離比較遠,需要在每一個機房各部署一套 Kubernetes。

這種情況下,Kubernetes 的管控依然要實現高可用,只不過跨機房的高可用就需要應用層來實現了。

在應用層,微服務的治理平臺,例如註冊發現,ZooKeeper 或者 Euraka,APM,配置中心等都需要實現跨機房的高可用。另外就是服務要跨機房部署,實現城市級機房故障遷移能力。

運維

運維一個大規模微服務系統也有不一樣的挑戰。

首先,建議使用的是 Kubernetes 編排的聲明式的運維方式,而非 Ansible 之類命令式的運維方式。

另外,對於系統的發佈,要進行灰度、藍綠發佈,降低系統上線發佈風險。要有這樣的理念,任何一個新上線的系統,都是不可靠的。

   

所以可以通過流量分發的模式,逐漸切換到新的服務,從而保障系統的穩定。

其三,完善監控及應對機制,對系統各節點、應用、組件全面地監控,能夠第一時間快速發現並解決問題。

   

監控絕非只有基礎設施的 CPU,網路,磁碟的監控,應用的,業務的,調用鏈的監控都應該有。

而且對於緊急事件,應該有應急預案,應急預案是在高可用已經考慮過之後,仍然出現異常情況下,應該採取的預案,例如三個 etcd 全掛了的情況。

其四,持續關註線上系統網路使用、伺服器性能、硬體存儲、中間件、資料庫燈指標,重點關註臨界狀態,也即當前還健康,但是馬上可能出問題的狀態。

例如網關 PPS 達到臨界值,下一步就要開始丟包了,資料庫快滿了,消息出現大量堆積等等。

DBA

對於一個線上業務系統來講,資料庫是重中之重,很多的性能瓶頸定位到最後,都可能是資料庫的問題。所以 DBA 團隊要對資料庫的使用,進行把關。

造成資料庫性能問題,一方面是 SQL 語句的問題,一方面是容量的問題。

例如查詢沒有被索引覆蓋,或者在區分度不大的欄位上建立的索引,是否持鎖時間過長,是否存在鎖衝突等等,都會導致資料庫慢的問題。

因而所有上線的 SQL 語句,都需要 DBA 提前審核,並且要對於資料庫的性能做持續的監控,例如慢 SQL 語句等。

另外對於資料庫中的數據量也要持續的監控,到一定的量就需要改分散式資料庫 DDB,進行分庫分表,到一定的階段需要對分散式資料庫進行擴容。

故障演練和性能壓測

再好的規劃也比不上演練,再好的性能評估也比不上線上的性能壓測。

性能問題往往是通過線上性能壓測發現的。線上壓力測試需要有一個性能測試的平臺,做多種形式的壓力測試。

例如容量測試,通過梯度的加壓,看到什麼時候實在不行。摸高測試,測試在最大的限度之上還能承受多大的量,有一定的餘量會保險一些,心裡相對比較有底。

再就是穩定性測試,測試峰值的穩定性,看這個峰值能夠撐一分鐘,兩分鐘還是三十分鐘。還有秒殺場景測試,限流降級演練測試等。

只有經過性能壓測,才能發現線上系統的瓶頸點,通過不斷的修複和擴容瓶頸點,最終才能知道服務之間應該以各種副本數的比例部署,才能承載期望的 QPS。

對於可能遇到的故障,可以進行故障演練,故意模擬一些故障,來看系統如何反應,是否會因為自修複,多副本,容錯等機制,使得這些故障對於客戶端來講沒有影響。

戰術設計

下麵,我們就從架構的每個層次,進行戰術設計。我們先來看一下高可用部署架構選型以及他們的優劣:

   

高可用性要求和系統的負載度和成本是強相關的。越簡單的架構,部署成本越低的架構,高可用性越小,例如上面的單體應用。

而微服務化,單元化,異地多活,必然導致架構複雜難以維護,機房成本比較高,所以要使用多少成本實現什麼程度的高可用,是一個權衡。

高可用的實現需要多個層次一起考慮:

   

首先是應用層,可以通過異地多活單元保證城市級高可用,這樣使得一個城市因為災難宕機的時候,另外一個城市可以提供服務。

另外每個多活單元採用雙機房保證機房級高可用,也即同城雙機房,使得一個城市中一個機房宕機,另一個機房可以提供服務。

再者每個機房中採用多副本保證實例級高可用,使得一個副本宕機的時候,其他的副本可以提供服務。

其次是資料庫層,在數據中心之間,通過主從複製或 MGR 實現數據非同步複製,在每個集群單元中採用 DDB 分庫分表,分庫分表中的每個實例都是有資料庫同步複製。

其三是緩存層,在數據中心之間,緩存採用多集群單元化複製,在每個集群單元中採用多副本主從複製。

其四微服務治理平臺層,平臺組件異地多活單元保證了城市級高可用,平臺組件每個多活單元採用雙機房保證機房級高可用,平臺組件每個機房中採用多副本保證實例級高可用。

當有了以上高可用方案之後,則以下的故障等級以及影響時間如下表格:

   

接下來,我們每個層次詳細論述。

應用層

下圖以最複雜的場景,假設有三個城市,每個城市都有兩個完全對等的數據中心。三個城市的數據中心也是完全對等的。

我們將整個業務數據按照某個維度分成 A,B,C 三部分。這樣任何一部分全部宕機,其他部分照樣可以提供服務。

對於有的業務,如果省級別的服務中斷完全不能忍受,市級別的服務中斷要求恢復時間相當短,而區縣級別的服務中斷恢復時間可以相對延長。

在這種場景下,可以根據地區來區分維度,使得一個區縣和另外一個區縣的數據屬於不同的單元。

為了節約成本,模型可能會更加簡化。中心節點和單元化節點不是對稱的。中心節點可以實現同城雙活,而異地單元化的部分只部署一個機房即可。這樣是能滿足大部分高可用性需求的。

這種架構要求實現中間件層和資料庫層單元化,這個我們後面會仔細講。

   

接入層

單元化要求 App 層或者在機房入口區域的接入層,實現中心單元和其他單元節點的流量分發。

對於初始請求沒有任何路由標記的,可以隨機分發給任何一個單元,也可以根據地區或者運營商在 GSLB 中分發給某個就近的單元。

應用層接收到請求以後,根據自己所在的單元生成路由信息,將路由信息返回給接入層或者 App。

接下來 App 或者接入層的請求,都會帶著路由信息,選擇相應的單元進行發送,從而實現了請求的處理集中在本單元。

   

中間件層

在中間件層,我們以 ZooKeeper 為例,分為以下兩個場景:

場景一:ZooKeeper 單元化主從多活

在這種場景下,主機房和單元化機房距離相隔較近,時延很小,可以當做一個機房來對待。可以採用 ZooKeeper 高可用保障通過多 ZooKeeper 實例部署來達成。

如圖所示,主機房 ZooKeeper 有 Leader 和 Follower,單元化機房的 ZooKeeper 僅為 Observer。

   

場景二:ZooKeeper 單元化多集群複製

兩個機房相距較遠,每個機房部署一套 ZooKeeper 集群,集群之間進行數據同步。

各機房應用連接機房內的 ZooKeeper 集群,註冊的信息通過數據同步,能夠被其他機房應用獲取到。

單一機房 ZooKeeper 集群不可用,其餘機房不受影響。當前不考慮做不同機房之間的集群切換。

   

資料庫層

在資料庫層,首先要解決的問題是,分散式資料庫 DDB 集群多機房同步複製。

在單元內採用同城主從複製模式,跨單元採用 DTS/NDC 實現應用層數據雙向同步能力。

   

對於數據的 ID 分配,應該採取全局唯一 ID 分配,有兩種實現方式,如果主機房和單元化機房距離較近,可採用 ID 分配依然採用中心式, 所有機房的單元全部向同一中心服務申請 ID 的方式。

如果主機房和單元化機房相隔較遠,可採用每個單元各自分配, 通過特定規則保證每個機房得到的最終 ID 不衝突的方式。

   

緩存層

在緩存層,有兩種方式:

方式一是集群熱備,新增 Redis 集群作為熱備份集群。

   

主集群與備份集群之間在服務端進行數據同步,通過 Redis Replication 協議進行同步處理。

離線監聽主集群狀態,探測到故障則進行主備之間切換,信息通過配置中心下達客戶端,類哨兵方式進行監聽探活。

在這種場景下,集群之間數據在服務端進行同步,正常情況下,集群之間數據會一致。但會存在一定的複製時延。

在故障切換時,可能存在極短時間內的數據丟失。如果將緩存僅僅當緩存使用,不要做記憶體資料庫使用,則沒有問題。

第二種方式,集群多活。新增集群作為多活集群,正常情況下客戶端根據 Key 哈希策略選擇分發到不同集群。

   

客戶端通過 Proxy 連接集群中每一個節點,Proxy 的用處是區分客戶端寫入與集群複製寫入。

集群之間在服務端進行數據雙向複製,數據變更通過 Redis Replication 協議獲取。

離線監聽主集群狀態,探測到故障則進行切換,信息通過配置中心下達客戶端,類哨兵方式進行監聽探活。

此方案應用於單純的集群間高可用時,同一個 Key 在同一段時間內只會路由到同一個集群,數據一致性可以保證。

在故障切換情況下,可能存在極端時間內的數據丟失。

微服務治理平臺

作為大規模微服務的微服務治理平臺,一方面自己要實現單元化,另外一方面要實現流量在不同單元之間的染色與穿梭。

從 API 網關,NSF 服務治理和管理中心,APM 性能管理,GXTS 分散式事務管理,容器平臺的管控都需要進行跨機房單元化部署。

   

當請求到達一個單元之後,API 網關上就帶有此單元的路由信息,NSF 服務治理與管理平臺在服務之間相互調用的時候,同樣會插入此單元的路由信息。

當一個單元某實例全掛的時候,可以穿梭到另一個單元進行調用,併在下一跳調用回本單元,這種方式稱為流量染色。

 

聯繫QQ:137071249

QQ群:793305035


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 背景 NPE問題,100%的Java程式員都碰到,並且曾經是心中的痛。 1965年英國TonyHoare引入了Null引用,後續的設計語言包括Java都保持了這種設計。 一個例子 業務模型 Person 有車一族, 有Car欄位, Car 車,每個車都有購買保險, 有Insurance欄位; Ins ...
  • 場景 在IDEA中新建SpringBoot項目後,修改了預設的Maven倉庫和配置文件,然後在啟動項目時提示: Error:java: 讀取\org\assertj\assertj-core\3.11.1\assertj-core-3.11.1.jar時出錯; 博客: https://blog.cs ...
  • #include<stdio.h> #include<stdlib.h> #include<math.h> #include<string.h> int main(void) { int n; printf("請輸入需要輸入的整數的數量:"); scanf("%d", &n); // 確定深度 in ...
  • Redis 的讀寫都是在記憶體中,所以它的性能較高,但在記憶體中的數據會隨著伺服器的重啟而丟失,為了保證數據不丟失,我們需要將記憶體中的數據存儲到磁碟,以便 Redis 重啟時能夠從磁碟中恢複原有的數據,而整個過程就叫做 Redis 持久化。 Redis 持久化也是 Redis 和 Memcached 的 ...
  • 發現問題 破解MyEclipse運行.cracker無法運行,切換java_home的配置路徑,運行Java version查看版本號,jdk版本始終都是jdk 10.0 問題根源 Java version命令運行的是java.exe,我們打開環境變數中的path 打開path的第一條目錄,發現裡面 ...
  • 背景 NPE問題,100%的Java程式員都碰到,並且曾經是心中的痛。 1965年英國TonyHoare引入了Null引用,後續的設計語言包括Java都保持了這種設計。 一個例子 業務模型 Person 有車一族, 有Car欄位, Car 車,每個車都有購買保險, 有Insurance欄位; Ins ...
  • 採集的站點: 免費代理IP http://ip.yqie.com/ipproxy.htm66免費代理網 http://www.66ip.cn/89免費代理 http://www.89ip.cn/無憂代理 http://www.data5u.com/雲代理 http://www.ip3366.net/ ...
  • 場景 在IDEA中新建SpringBoot項目,後啟動項目時提示: Error:(3, 32) java: 程式包org.springframework.boot不存在 實現 將pom.xml中parent依賴版本降低,這裡改為2.1.6,然後在右邊Maven面板中點擊Reimport All Ma ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...