分散式系統設計策略

女友在高考發表於2022-02-27

服務呼叫方式

1. HTTP協議的通訊框架

1. HttpURLConnection

HttpURLConnection是java原生支援的。

2. Apache Common HttpClient

HttpClient是Apache Common下的,可以用來提供高效的、功能豐富的HTTP協議的客戶端程式設計工具包。

  • 實現了所有的HTTP方法(GET、POST、PUT、HEAD等)
  • 支援HTTPS協議
  • 支援代理伺服器

3. OKhttp3

OKHttp是一個當前主流的網路請求開源框架,可以替代HttpUrlConnection和Apache HttpClient

  • 支援http2.0,對一臺機器的請求共享一個socket
  • 採用連線池技術,可以有效的減少http連線數量
  • 無縫整合GZIP壓縮技術
  • 支援Response Cache,避免重複請求
  • 域名多IP支援

4. RestTemplate

Spring RestTemplate是Spring提供的用於訪問Rest服務的客戶端。

  • 面向URL元件,必須依賴於主機+埠+URI
  • RestTemplate不依賴於服務介面,僅關注REST響應內容
  • Spring Cloud Feign通訊在使用RestTemplate

2. RPC框架

1. Java RMI

2. Hessian

Hessian是一個輕量級的remoting on http工具,使用簡單的方法提供了RMI的功能。採用的是二進位制RPC協議。

3. Dubbo

Dubbo是阿里巴巴公司開源的一個高效能優秀的服務框架,使得應用可通過高效能的 RPC 實現服務的輸出和輸入功能,可以和Spring框架無縫整合。Dubbo是一款高效能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現

4. gRPC

gRPC是由Google公司開源的一款高效能的遠端過程呼叫(RPC)框架,可以在任何環境下執行。該框架提供了負載均衡,跟蹤,智慧監控,身份驗證等功能,可以實現系統間的高效連線

3. 跨域

在分散式系統中,會有呼叫其他業務系統的場景,導致出現跨域問題。跨域實質上是瀏覽器的一種保護處理請求是可以正常發起的,服務端也是正常接受,只是瀏覽器對其進行了攔截,導致響應內容不可用。

產生跨域的幾種情況如下:

當前頁面URL 被請求頁面URL 是否跨域 原因
http://www.lagou.com/ https://www.lagou.com/ 跨域 協議不同(http、https)
http://www.lagou.com/ http://www.baidu.com 跨域 主域名不同
http://www.lagou.com/ http://kuai.lagou.com 跨域 子域名不同
http://www.lagou.com:8080 http://www.lagou.com:8090 跨域 埠不同

常見的解決方案

  1. 使用jsonp解決網站跨域

缺點:不支援post請求,程式碼書寫比較複雜

  1. 使用HttpClient內部轉發

  2. 使用設定響應頭允許跨域

response.setHeader(“Access-Control-Allow-Origin”, “*”); 設定響應頭允許跨域.

  1. 基於Nginx搭建介面API閘道器

  2. 使用Zuul搭建微服務API閘道器

分散式服務治理

1. 服務協調

分散式協調技術主要用來解決分散式環境當中多個程式之間的同步控制,讓他們有序的去訪問某種臨界資源,防止造成“髒資料”的後果。

分散式鎖兩種實現方式:

  1. 基於Redis實現分散式鎖
  2. 基於ZooKeeper實現

2. 服務削峰

削峰從本質上來說就是更多的延緩使用者請求,以及層層過濾使用者的訪問需求,遵從“落地到資料庫的請求數要儘量少”的原則

  1. 訊息佇列
  2. 流量削峰漏斗:通過CDN->快取系統->後臺系統->DB ,層層減少後打到DB的流量就少了

3. 服務降級

整個架構整體的負載超出了預設的上限閾值時,為了保證重要或基本的服務能正常執行,我們可以將一些不重要或不緊急的服務或任務進行服務的延遲使用或暫停使用

降級方案:

  • 頁面降級 :視覺化介面禁用點選按鈕、調整靜態頁面
  • 延遲服務:如定時任務延遲處理、訊息入MQ後延遲處理
  • 寫降級:直接禁止相關寫操作的服務請求
  • 讀降級:直接禁止相關讀服務請求
  • 快取降級:使用快取方式來降級部分讀頻繁的介面

後端程式碼層的處理方式:

  • 拋異常
  • 返回NULL
  • 呼叫Mock資料
  • 呼叫Fallback處理邏輯

可以將服務架構進行故障風暴分級,根據具體的等級進行降級

4. 服務限流

限流是為了對併發請求進行限制來保護系統。一旦達到限制我們可以拒絕服務、排隊或等待

限流演算法:

1. 固定視窗計數器

計數器限制每時間內(如一分鐘內)請求數不超過一定的值,再下一時間內計數器清零重新計算

存在問題:已知客戶端限流1分鐘100次。如果客戶端在第一分鐘的59秒請求了100次,又在第二分鐘的第1秒請求了100次,這樣兩秒內後端就會受到200次請求的壓力

2. 滑動視窗計數器

針對固定時間演算法會在臨界點存在瞬間大流量衝擊的場景,滑動時間視窗計數器演算法應運而生。它將時間視窗劃分為更小的時間片段,每過一個時間片段,時間視窗就右滑一格,每個時間片段都有獨立的計數器。我們計算整個時間視窗內的請求總數時會累加所有時間片段內的計數器。

時間視窗劃分的越細,那麼滑動視窗的滾動就越平滑。

下面舉例說明滑動視窗是如何解決臨界時間瞬間流量的問題:

一個時間視窗是60s,請求數最大100次。我們分為了3個時間片段。

在0:59時打入了100個請求,落在第3個格子(灰色標記)上(統計0:40-1:00的請求)。1:00的時候視窗右滑,1:01時又打入100個請求,此時打到了第四個(藍色標記)格子(統計1:01-1:20)。此時統計整個時間視窗的流量是200個,後續的請求將拒絕。

3. 漏桶演算法

漏桶演算法類似一個限制出水速度的水桶,不管你放多少水,我都是勻速出水,當桶滿了,就會溢位。

實現:一個固定大小的FIFO佇列,定時取元素,佇列滿了再加入請求直接拒接。

  • 優點:可以削峰,不會出現流量突刺現象。
  • 缺點:桶佇列中的請求會排隊,響應時間變長。

4. 令牌桶演算法

令牌桶演算法是以一個恆定的速度往桶裡放令牌,桶滿了就廢棄,每進來一個請求就去桶裡拿令牌,拿到令牌就可以處理請求,沒有令牌了就拒絕請求。

  • 優點:應對突發流量,桶裡有令牌時可以快速響應
  • 缺點:相對漏桶一定程度上對下游的保護沒那麼大

5. 服務熔斷

在網際網路系統中,當下遊系統變慢或當機時,上游服務為了保護系統整體的可用性,可以暫時切斷對下游服務的呼叫。這種犧牲區域性,保全整體的措施就叫做熔斷。

一般的熔斷機制:

  1. 開啟熔斷

在固定時間視窗內,介面呼叫超時比率達到一個閾值,會開啟熔斷。進入熔斷狀態後,後續對服務介面的呼叫不再經過網路,直接執行本地的預設方法,達到服務降級的效果。
2. 熔斷恢復

熔斷不可能是永久的,當經過了規定時間之後,服務將從熔斷狀態恢復過來,再次接受呼叫方的遠端呼叫。

Spring Cloud Hystrix

Spring Cloud Hystrix是基於Netflix的開源框架Hystrix實現,該框架實現了服務熔斷、執行緒隔離等一系列服務保護功能。

對於熔斷機制的實現,Hystrix設計了三種狀態:

  • 熔斷關閉狀態(Closed)
    服務沒有故障時,熔斷器所處的狀態,對呼叫方的呼叫不做任何限制。
  • 熔斷開啟狀態(Open)
    在固定時間內(Hystrix預設是10秒),介面呼叫出錯比率達到一個閾值(Hystrix預設為50%),會進入熔斷開啟狀態。進入熔斷狀態後,後續對該服務介面的呼叫不再經過網路,直接執行本地的fallback方法。
  • 半熔斷狀態(Half-Open)
    在進入熔斷開啟狀態一段時間之後(Hystrix預設是5秒),熔斷器會進入半熔斷狀態。所謂半熔斷就是嘗試恢復服務呼叫,允許有限的流量呼叫該服務,並監控呼叫成功率。如果成功率達到預期,則說明服務已恢復,進入熔斷關閉狀態;如果成功率仍舊很低,則重新進入熔斷開啟狀態。

三個狀態的轉化關係如下圖:

Sentinel

Sentinel和Hystrix的原則是一致的,當呼叫鏈路中某個資源出現不穩定,例如,表現為timeout,異常比例升高的時候,則對這個資源的呼叫進行限制,並讓請求快速失敗,防止影響到其他的資源。

Sentinel熔斷手段:

  • 通過對併發執行緒數進行限制
  • 通過響應時間對資源降級

Sentinel和Hystrix對比

6. 鏈路追蹤

分散式微服務架構上通過業務來劃分服務的,通過REST呼叫對外暴露的一個介面,可能需要很多個服務協同才能完成這個介面功能。隨著服務的越來越多,對呼叫鏈的分析會越來越複雜。

鏈路追蹤的作用:

  1. 故障快速定位
  2. 分析各個呼叫環節的效能
  3. 資料分析,可以分析使用者的行為路徑。
  4. 生成服務呼叫拓補圖

鏈路跟蹤設計原則

  1. 設計目標
  • 低侵入性,應用透明
  • 低損耗
  • 大範圍部署,擴充套件性
  1. 埋點和生成日誌

埋點即系統在當前節點的上下文資訊,可以分為客戶端埋點、服務端埋點,以及客戶端和服務端雙向型埋點。埋點日誌通常要包含以下內容:TraceId、RPCId、呼叫的開始時間,呼叫型別,協議型別,呼叫方ip和埠,請求的服務名等資訊;呼叫耗時,呼叫結果,異常資訊,訊息報文等
3. 抓取和儲存日誌

日誌的採集和儲存有許多開源的工具可以選擇,一般來說,會使用離線+實時的方式去儲存日誌,主要是分散式日誌採集的方式。典型的解決方案如Flume結合Kafka。

  1. 分析和統計呼叫鏈資料
    一條呼叫鏈的日誌散落在呼叫經過的各個伺服器上,首先需要按 TraceId 彙總日誌,然後按照RpcId對呼叫鏈進行順序整理。呼叫鏈資料不要求百分之百準確,可以允許中間的部分日誌丟失。

  2. 計算和展示
    彙總得到各個應用節點的呼叫鏈日誌後,可以針對性的對各個業務線進行分析。需要對具體日誌進行整理,進一步儲存在HBase或者關係型資料庫中,可以進行視覺化的查詢。

大的網際網路公司都有自己的分散式跟蹤系統,比如Google的Dapper,Twitter的zipkin,淘寶的鷹眼,新浪的Watchman,京東的Hydra,國內開源愛好者開源的Skywalking。

相關文章