Spring Cloud Hystrix 服務容錯保護

c旋兒發表於2019-05-28

一、Hystrix 是什麼

​ 在微服務架構中,我們將系統拆分成了若干弱小的單元,單元與單元之間通過HTTP或者TCP等方式相互訪問,各單元的應用間通過服務註冊與訂閱的方式相互依賴。由於每個單元都在不同的程式中執行,依賴遠端呼叫的方式執行,這樣就可能引起因為網速變慢或者網路故障導致請求變慢或超時,若此時呼叫方的請求在不斷增加,最後就會因等待出現故障的依賴方響應形成任務積壓,最終導致自身服務的癱瘓。

Hystrix 是Netflix 中的一個元件庫,它隔離了服務之間的訪問點,阻止了故障節點之間可能會引起的雪崩效應,並提供了後備選項。

​ 在微服務架構中,存在著許多的服務單元,若單一節點的故障,就很容易因為依賴關係而引發故障的蔓延,最終導致整個生態系統的癱瘓。為了解決這樣的問題,產生了斷路器等一系列的保護機制措施。

​ 在分散式架構中,斷路器模式的作用也是類似的,當某個服務單元發生故障(類似用電器發生短路)之後,通過斷路器的故障監控(類似熔斷保險絲),向呼叫方返回一個錯誤響應,而不是長時間的等待。這樣就不會使得執行緒因呼叫故障服務被長時間佔用不釋放,避免了故障在分散式系統中的蔓延。

雪崩效應

​ 雪崩效應就像是水滴石穿,蝴蝶效應一樣,是指微小的事物隨著時間的推移,會變得越來越巨大,從而對整個環境造成影響的現象。例如:在生態系統中,某一類物種的滅絕可能對整個生態系統造成不了太大的損失,但是這類物種的滅絕可能會引發其他物種的死亡,其他物種的滅絕又會影響另外一種物種的滅亡,就像雪球越滾越大,最終會導致整個生態系統的崩潰。

Spring Cloud Hystrix 服務容錯保護

如上圖所示:A作為服務提供者,B為A的服務消費者,C和D是B的服務消費者。A不可用引起了B的不可用,並將不可用像滾雪球一樣放大到C和D時,雪崩效應就形成了。

雪崩效應產生場景

  • 流量激增: 比如異常流量,使用者重試導致系統負載升高;
  • 快取重新整理: 假設A為client端,B為Server端,假設A系統請求都流向B系統,請求超出了B系統的承載能力,就會造成B系統崩潰
  • 連線未釋放: 程式碼迴圈呼叫的邏輯問題,資源未釋放引起的記憶體洩漏等問題;
  • 硬體故障: 比如當機,機房斷電等
  • 執行緒同步等待: 系統間經常採用同步服務呼叫模式,核心服務和非核心服務共用一個執行緒池和訊息佇列。如果一個核心業務執行緒呼叫非核心業務執行緒,這個非核心執行緒交由第三方系統完成,當第三方系統本身出現問題,導致核心執行緒阻塞,一直處於等待狀態,而程式間的呼叫是有超時限制的,最終這條執行緒將斷掉,也可能引發雪崩;

常見解決方案

​ 針對上述的雪崩問題,每一條都有一個自己的解決方案,但是任何一個解決方案能夠應對所有場景

  • 針對流量激增,採用自動擴容以應對流量激增,或者在負載均衡器上安裝限流模組

  • 針對快取重新整理,參考Cache應用的服務過載案例研究

  • 針對硬體故障,採用多機房災備,跨機房路由

  • 針對同步等待,採用執行緒隔離,熔斷器等機制

    通過實踐發現,執行緒同步等待是最常見引發的雪崩效應的場景。

二、Hystrix斷路器搭建

​ 在開始使用Spring Cloud Hystrix斷路器之前,我們先用之前實現的一些內容作為基礎,構建一個如下圖所示的服務呼叫關係:

Spring Cloud Hystrix 服務容錯保護

如圖所示,上面需要的角色有三個,服務有四個

  • ribbon-connsumer: ribbon消費者,消費server-provider提供的服務
  • server-provider: 服務提供者,提供服務供消費者消費(有點像父母默默的付出一樣),啟動兩個例項,還記得怎麼啟動嗎?—server.port 啟動
  • eureka-server: eureka註冊中心,提供最基本的訂閱釋出功能。消費者和服務提供者都需要往註冊中心註冊自己

​ 依次啟動上面的四個服務,發現註冊中心已經成功註冊了四個服務(包括自己)

Spring Cloud Hystrix 服務容錯保護

  • 呼叫http://localhost:9000/ribbon-consumer 發現能夠通過Ribbon進行遠端呼叫
  • 在未加入斷路器之前,關閉ribbon-consumer 的連線,再次呼叫http://localhost:9000/ribbon-consumer,發現服務無法提供(使用Postman 測試)

Spring Cloud Hystrix 服務容錯保護

下面開始引入Hystrix

  • 在ribbon-consumer 工程的pom.xml的dependency節點下引入spring-cloud-starter-hystrix依賴

  • 在ribbon-consumer 工程的主載入類中新增@EnableCircuitBreaker開啟斷路器的功能

    注意:這裡也可以使用@SpringCloudApplication註解來修飾應用主類,具體定義如下

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableCircuitBreaker
    public @interface SpringCloudApplication {}

    SpringCloudApplication 註解上有@EnableCircuitBreaker 註解,用來開啟斷路器的功能,其他主要註解是@SpringBootApplication ,這個註解是SpringBoot的啟動類註解, @EnableDiscoveryClient該註解可以發現Eureka註冊中心

  • 改造消費方式,新增HystrixService類,並且注入RestTemplate例項,然後,將在RibbonController中對RestTemplate 的使用遷移到hystrixService方法中,最後,在hystrixService上新增@HystrixCommand註解來指定回掉方法。

    // HystrixService
    @Service
  public class HystrixService {

      @Resource
      RestTemplate restTemplate;
            
        // 指定回掉方法是下面的hystrixCallback
      @HystrixCommand(fallbackMethod = "hystrixCallBack")
      public String hystrixService(){
          return restTemplate.getForEntity("http://server-provider/hystrix",String.class).getBody();
      }

      public String hystrixCallBack(){
          return "error";
      }
  }
        
  • 服務提供者的業務非常簡單,具體程式碼如下
        @RequestMapping(value = "/hystrix", method = RequestMethod.GET)
    public String hystrix(){
        return "hystrix";
    }
  • 下面來驗證一下通過斷路器的回掉實現,重啟之前關閉的8081埠,恢復成為四個服務的狀態,並確保http://localhost:9000/ribbon-consumer/ 能夠提供服務,並且以輪詢的方式迴圈訪問8081 和 8082 埠的服務。此時斷開8081埠,發現頁面上展示的不再是 hystrix ,而是"error",而另一個服務是正常能夠列印。

三、斷路器優化

​ 經過以上服務的搭建,相信你已經能夠搭建出來最基本的Hystrix熔斷器,並且實現了服務熔斷機制,下面就來對斷路器做一下簡單的優化,來模擬服務阻塞(長時間未響應)的情況。

優化server-provider程式碼如下:

        
        @RequestMapping(value = "/hystrix", method = RequestMethod.GET)
    public String hystrix() throws InterruptedException {
        ServiceInstance serviceInstance = discoveryClient.getLocalServiceInstance();
        // 讓執行緒等待幾秒鐘
        int sleepTime = new Random().nextInt(3000);
        Thread.sleep(sleepTime);
        System.out.println("weak up!!!");
        log.info("sleepTime = " + sleepTime);
        return "hystrix";
    }

依次啟動所有的服務,在主頁上訪問 http://localhost:9000/ribbon-consumer ,多次重新整理主頁,發現error 和 hystrix 是交替出現的,這是為何?

因為hystrix斷路器的預設超時時間是2000毫秒,所以這裡採用了0 - 3000 的隨機數,也就是訪問請求在 0 -2000 毫秒內是不超時的,不會觸發斷路器,而> 2000 毫秒是超市的,預設會觸發斷路器。

參考資料:

http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html

https://www.cnblogs.com/vveiliang/p/6643907.html

《Spring Cloud 微服務實戰》

相關文章