微服務-高併發狀況下接口降級和熔斷策略

介紹一降低級和熔斷的概念

什麼是降級呢? 降級意味着多種方案,當系統出現問題的時候,你有一個備選方案能夠立刻切換,好比有一個接口的功能是實時預測將來一個月某個商品的採購數量,忽然間依賴的上游系統出現問題了,那麼咱們的接口就徹底不可用了嗎?顯然這是不該該的,這時我接口就能夠降級,返回昨天實時計算出來的結果,雖然準確性可能差一點,但系統可以正常運轉,降級也分爲自動降級和手動降級,前者是系統自動檢測到問題時自動切換,後者是系統檢測到問題報警,人爲的切換,降級表明着系統相比降級以前其功能表現不如以前的完美(這個具體體如今功能準確性,可用性上等,如上面接口的例子)
什麼是熔斷呢? 通俗來說,熔斷指的是遇到危險了,必須立刻停掉,好比生活中的電流過大,必須立刻切斷,不然就發生了火災了,熔斷以後就會致使斷電,徹底不可用,在一個系統中,假設一個接口部署了10臺機器(分佈式),忽然某一臺機器的接口調用狀況正確率降到90%,那麼這臺機器確定出現問題了,這個時候就須要熔斷這臺機器,把這臺機器從整個集羣中摘掉,從而保證用戶的請求100%的正確,再好比,一個系統中有不少功能,這些功能有些是核心功能,有些是非核心功能,那麼在一些大促中,咱們可能熔斷掉一些非核心功能,從而保證核心功能的流轉(登陸和註冊,登陸屬於核心,註冊是屬於非核心)
git

爲何須要降級和熔斷

不論是降級仍是熔斷,都是爲了保證了系統的穩定性,可用性。降級每每表明系統功能部分不可用,熔斷表明的是徹底不可用,再舉一個簡單例子,註冊功能,降級可能出現的狀況是手機號能夠正常,郵箱不能註冊,而熔斷出現的狀況是註冊功能徹底不可用,因此說有時候熔斷是一種特殊的降級。這在整個系統設計編碼中都須要考慮到。github

經常使用的降級和熔斷策略

在業務系統時,降級在編碼時須要考慮好備選方案,和業務確認方案的合理性,熔斷在編碼時須要分離核心功能和非核心功能,梳理上下游依賴關係,防止強依賴引發的系統的雪崩,這些是業務系統功能設計時須要常常考慮的。算法

全部業務系統都須要考慮的東西,就意味着能夠優化,能夠剝離,抽象出來,作成公共組件,中間件,造成通用性。api

上面有提到,降級和熔斷的最終目的都是保證系統的穩定性,可靠性,保證核心服務可用,那麼在造成中間件時具體措施是什麼呢?併發

降級
  1. 超時降級(調用服務時超時返回默認值或者其它處理辦法)
  2. 失敗次數降級(服務可用率降低時降級)
  3. 限流也是降級的一種辦法
  4. 故障降級(依賴的外系統發生故障時降級)
  5. 拒絕服務降級
熔斷
  1. 系統攻擊熔斷(當某個服務遭遇流量攻擊時,能夠熔斷這個服務)
  2. 涉及核心功能運行時的熔斷(下單和評論功能,關鍵時刻能夠熔斷評論功能)

無論降級仍是熔斷,在設計時都要考慮:降級熔斷算法,恢復機制,報警。這些是必備的,不能系統降級了或者熔斷了就沒法回覆以前的狀況,也不能不報警,要否則開發人員都不知道,這還得了。異步

熔斷和降級的異樣性
  1. 二者的目的至關
  2. 二者的最終的表現的相同
  3. 粒度同樣,大多數都是服務級別的粒度,也有多是方法級別的
  4. 自治性要求比較高(儘量的智能化)
  5. 降級通常是客戶端處理,熔斷是在服務端處理的
設計方案

介紹一種的常見的方案,服務碼+配置中心,調用任何服務時都傳入必要參數服務碼和開關,默認關閉,當觸發某種條件時可打開開關,或者經過配置中心手動推送開關新的值,從而保護系統不被單個服務壓垮,別看這個簡單,不少系統都是這麼作的。分佈式

func  DowngradeAndFuse (ctx context.Context){
    //業務碼
    bizValue := ctx.Value("bizCode")
    //熔斷降級標識
    flag := ctx.Value("flag")
   
    if bizValue == "指定業務" && flag {
       //降級或者熔斷
       return
    }
}

Hystrix的原理

Hystrix有Java和Go版本的,Java版本的是Netflix公司開發並開源的,Go版本的是由afex(我的)建立的,代碼庫地址以下:函數

https://github.com/Netflix/Hystrix
https://github.com/afex/hystrix-go

Hystrix引入如下手段來保護系統:優化

  1. 資源隔離(線程池和信號量兩種手段的隔離)
  2. 限流
  3. 降級
  4. 熔斷(斷路器)

Hystrix如何設計實現這些手段呢?編碼

  1. 使用命令模式將全部對外部服務(或依賴關係)的調用包裝在HystrixCommand或HystrixObservableCommand對象中,並將該對象放在單獨的線程中執行
  2. 每個依賴都有本身對應的線程池或者信號量,線程池耗盡時,拒絕請求
  3. 維護請求的各類狀態(成功,失敗,超時的次數)
  4. 當錯誤率到達必定閾值時,進行熔斷,過必定的時間後又恢復
  5. 提供降級,失敗,成功,熔斷後的回調邏輯
  6. 實時的監控指標和配置信息的修改

用代碼實現一個hystrix-go的Demo,第一步寫在init初始化中,配置hystrix的一些參數,若是不配置的話,也會有默認參數。

func init() {
   hystrix.ConfigureCommand("my_command", hystrix.CommandConfig{
      //多長時間 超時
      Timeout: 5000,
      //最大併發數
      MaxConcurrentRequests: 1,
      //錯誤百分比,錯誤率達到這個數值開啓熔斷
      ErrorPercentThreshold: 25,
      //當熔斷器被打開後,SleepWindow的時間就是控制過多久後去嘗試服務是否可用了(毫秒)
      SleepWindow: 10,
      //最小請求數,只有到達這個數量後才判斷是否開啓熔斷
      RequestVolumeThreshold: 10,
   })
}

如何使用hystrix熔斷呢,總的來講分爲4個步驟:
第一步:定義你調用的外部系統的服務
第二步:設置回調函數(當超時或者熔斷了會調用回調函數)
第三步:使用hystrix的api調用第一步定義好的服務
第四步:獲取最終結果(結果可能時正確的,也多是一個err)



//異步調用
func HystrixAsyStudy() {
   //第一步:
   result := make(chan string, 1)
   //定義依賴外部系統的函數
   f1 := func() error {
      // 處理業務系統(調用外部服務)
      fmt.Println("處理業務邏輯")
      result <- "處理結果"
      return nil
   }
   //第二步:
   //回調函數,只有 err不爲空,纔會執行回調函數(若是發生了超時,熔斷,
   //限流,超時以後也會回調)
   fallBack1 := func(err error) error {
      fmt.Println("回調函數")
      return err
   }
   //第三步:
   errors := hystrix.Go("my_command", f1, fallBack1)
   //第四步:
   select {
   case r := <-result:
      fmt.Println(r)
   case e := <-errors:
      fmt.Println(e)
   }
}

Hystrix更加詳細的文檔參考以下地址:

//Java版本的
https://github.com/Netflix/Hystrix/wiki/How-To-Use#Common-Patterns-FailFast
//Go版本的
https://github.com/afex/hystrix-go
相關文章
相關標籤/搜索