[菜鳥SpringCloud實戰(zhàn)入門]第五章:熔斷器Hystrix的使用 + 可視化監(jiān)控Hystrix Dashboard和Turbine

歡迎來到菜鳥SpringCloud實戰(zhàn)入門系列(SpringCloudForNoob),該系列通過層層遞進的實戰(zhàn)視角,來一步步學習和理解SpringCloud。

本系列適合有一定Java以及SpringBoot基礎的同學閱讀。

每篇文章末尾都附有本文對應的Github源代碼,方便同學調試。

實戰(zhàn)版本

  • SpringBoot:2.0.3.RELEASE
  • SpringCloud:Finchley.RELEASE

熔斷器Hystrix的使用

熔斷器的概念

關于熔斷器的解釋,這里引用一大段:

熔斷器適用于實現快速失敗,如果它在一段時間內偵測到許多類似的錯誤,會強迫其以后的多個調用快速失敗,不再訪問遠程服務器,從而防止應用程序不斷地嘗試執(zhí)行可能會失敗的操作,使得應用程序繼續(xù)執(zhí)行而不用等待修正錯誤,或者浪費CPU時間去等到長時間的超時產生。熔斷器也可以使應用程序能夠診斷錯誤是否已經修正,如果已經修正,應用程序會再次嘗試調用操作。

熔斷器模式就像是那些容易導致錯誤的操作的一種代理。這種代理能夠記錄最近調用發(fā)生錯誤的次數,然后決定使用允許操作繼續(xù),或者立即返回錯誤。 熔斷器開關相互轉換的邏輯如下圖:

在這里插入圖片描述

熔斷器就是保護服務高可用的最后一道防線。

Hystrix特性

1.斷路器機制

斷路器很好理解, 當Hystrix Command請求后端服務失敗數量超過一定比例(默認50%), 斷路器會切換到開路狀態(tài)(Open). 這時所有請求會直接失敗而不會發(fā)送到后端服務. 斷路器保持在開路狀態(tài)一段時間后(默認5秒), 自動切換到半開路狀態(tài)(HALF-OPEN). 這時會判斷下一次請求的返回情況, 如果請求成功, 斷路器切回閉路狀態(tài)(CLOSED), 否則重新切換到開路狀態(tài)(OPEN). Hystrix的斷路器就像我們家庭電路中的保險絲, 一旦后端服務不可用, 斷路器會直接切斷請求鏈, 避免發(fā)送大量無效請求影響系統(tǒng)吞吐量, 并且斷路器有自我檢測并恢復的能力.

2.Fallback

Fallback相當于是降級操作. 對于查詢操作, 我們可以實現一個fallback方法, 當請求后端服務出現異常的時候, 可以使用fallback方法返回的值. fallback方法的返回值一般是設置的默認值或者來自緩存.

3.資源隔離

在Hystrix中, 主要通過線程池來實現資源隔離. 通常在使用的時候我們會根據調用的遠程服務劃分出多個線程池. 例如調用產品服務的Command放入A線程池, 調用賬戶服務的Command放入B線程池. 這樣做的主要優(yōu)點是運行環(huán)境被隔離開了. 這樣就算調用服務的代碼存在bug或者由于其他原因導致自己所在線程池被耗盡時, 不會對系統(tǒng)的其他服務造成影響. 但是帶來的代價就是維護多個線程池會對系統(tǒng)帶來額外的性能開銷. 如果是對性能有嚴格要求而且確信自己調用服務的客戶端代碼不會出問題的話, 可以使用Hystrix的信號模式(Semaphores)來隔離資源.

使用Feign Hystrix

依然使用上一章建立的子模塊:Service-Feign

pom文件不需要變化,因為spring-cloud-starter-openfeign已經自帶了Hystrix。

修改配置文件application.yml,增加如下:

feign:
  hystrix:
    enabled: true

在我這里,由于Springboot使用了2.0.3,引入的是openfeign,所以出現了:

在這里插入圖片描述

起初以為這條指令無法生效,折騰了之后才發(fā)現是生效的。

推測是openfeign的問題,不是spring boot 1.X的spring-cloud-starter-feign,IDEA判斷失誤了。

接著新建類ServiceHiHystrix繼承ServiceHi接口,里面實現了失敗下的返回信息:

import org.springframework.stereotype.Component;

@Component
public class ServiceHiHystrix implements ServiceHi {
    @Override
    public String sayHiFromServiceHi(String name) {
        return "hello" + name +", this message send failed";
    }
}

更改ServiceHi接口,添加上fallback類為剛才新建的類:

@FeignClient(value = "service-hi", fallback = ServiceHiHystrix.class)

大功告成,啟動server-feign,eureka-hi和eureka測試:

在這里插入圖片描述

打開:http://localhost:8765/hello/rude3knife

在這里插入圖片描述

接著,關掉服務提供者eureka-hi,來模擬服務宕機的情況,重新訪問:http://localhost:8765/hello/rude3knife

在這里插入圖片描述

返回了新建的錯誤信息。

可視化熔斷器監(jiān)控Hystrix Dashboard和Turbine

Hystrix Dashboard

Hystrix-dashboard是一款針對Hystrix進行實時監(jiān)控的工具,通過Hystrix Dashboard我們可以在直觀地看到各Hystrix Command的請求響應時間, 請求成功率等數據。

我們只需要在上一章中的server-feign模塊繼續(xù)修改,因為上一章已經在該模塊加了熔斷器。

首先添加依賴:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <!--spring boot 1.X:spring-cloud-starter-hystrix-->
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <!--spring boot 1.X:spring-cloud-starter-hystrix-dashboard-->
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

啟動類修改:

@EnableHystrixDashboard
@EnableCircuitBreaker

啟動工程訪問:

http://localhost:8765/hystrix,將會看到如下界面:

在這里插入圖片描述

圖中會有一些提示:

大概意思就是如果查看默認集群使用第一個url,查看指定集群使用第二個url,單個應用的監(jiān)控使用最后一個,我們暫時只演示單個應用的所以在輸入框中輸入: http://localhost:8765/hystrix.stream ,輸入之后點擊 monitor,進入頁面。

如果沒有請求會先顯示Loading ...,訪問http://localhost:9001/hystrix.stream 也會不斷的顯示ping。

請求服務http://localhost:8765/hello/rude3knife,就可以看到監(jiān)控的效果了,首先訪問http://localhost:8765/hystrix.stream,顯示如下:

在這里插入圖片描述

出錯了!

隨機谷歌了下問題,網友給出了問題解決方案:

https://blog.csdn.net/ddxd0406/article/details/79643059

答主通過查看源碼后給出的解決方案是:

在這里插入圖片描述

評論區(qū)給出更為方便且優(yōu)雅的方案:

在配置文件中加入management.endpoints.web.exposure.include=*,將端口暴露就不需要聲明bean了,訪問地址要變一下/actuator/hystrix.stream

于是我們在yml中加入:

# 熔斷器DashBoard: actuator在boot2.0調整后開關web端點的配置,*代表開啟所有
management:
  endpoints:
    web:
      exposure:
        include: "*"

重新運行模塊,然后在網址一欄輸入:

http://localhost:8765/actuator/hystrix.stream

頁面會不停顯示Loading,不用慌,不顯示Unable to connect to Command Metric Stream.了。顯示Loading是因為因為還沒有請求過來。

之后我們嘗試發(fā)一次請求,在地址欄對server-feign的8765端口發(fā)送請求:

http://localhost:8765/hello/1232312

再來看之前的頁面,大功告成。

在這里插入圖片描述

到此單個應用的熔斷監(jiān)控已經完成。

Turbine

但是只使用Hystrix Dashboard的話, 你只能看到單個應用內的服務信息, 這明顯不夠. 我們需要一個工具能讓我們匯總系統(tǒng)內多個服務的數據并顯示到Hystrix Dashboard上, 這個工具就是Turbine.

在復雜的分布式系統(tǒng)中,相同服務的節(jié)點經常需要部署上百甚至上千個,很多時候,運維人員希望能夠把相同服務的節(jié)點狀態(tài)以一個整體集群的形式展現出來,這樣可以更好的把握整個系統(tǒng)的狀態(tài)。

在這里插入圖片描述

注意:

為了演示Turbine,在這里重新新建一個子模塊,名為hystrix-dashboard-turbine,創(chuàng)建步驟請看之前的第一章。

創(chuàng)建新模塊作為單獨的監(jiān)控節(jié)點模塊,是因為演示dashboard的時候是耦合在了server-feign中,而Turbine需要同時監(jiān)控多個服務消費者,把他耦合在一個消費者里顯得不合時宜。

創(chuàng)建后,pom.xml中引入:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <!--spring boot 1.X:spring-cloud-starter-hystrix-dashboard-->
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-netflix-turbine</artifactId>
</dependency>

啟動類中@EnableTurbine和@EnableHystrixDashboard:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;

@SpringBootApplication
@EnableTurbine
@EnableHystrixDashboard
public class HystrixDashboardTurbineApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardTurbineApplication.class, args);
    }

}

yml中:

server:
  # 服務端口號
  port: 8767
spring:
  application:
    # 服務名,即serviceId
    name: hystrix-dashboard-turbine
eureka:
  client:
    serviceUrl:
      # 安全認證的服務注冊中心地址
      defaultZone: http://localhost:8761/eureka
# 熔斷器turbine
turbine:
  aggregator:
    cluster-config: default
  cluster-name-expression: new String("default")
  app-config: SERVICE-FEIGN, SERVICE-FEIGN-2

代碼重點:

  • 新模塊端口號為:8767
  • turbine.appConfig :配置Eureka中的serviceId列表,表明監(jiān)控哪些服務(這里指定為SERVICE-FEIGN, SERVICE-FEIGN-2,稍后說)
  • turbine.aggregator.clusterConfig :指定聚合哪些集群,多個使用”,”分割,默認為default。可使用http://.../turbine.stream?cluster={clusterConfig之一}訪問
  • turbine.clusterNameExpression :
    • clusterNameExpression指定集群名稱,默認表達式appName;此時:turbine.aggregator.clusterConfig需要配置想要監(jiān)控的應用名稱;
    • 當clusterNameExpression: default時,turbine.aggregator.clusterConfig可以不寫,因為默認就是default;
    • 當clusterNameExpression: metadata[‘cluster’]時,假設想要監(jiān)控的應用配置了eureka.instance.metadata-map.cluster: ABC,則需要配置,同時turbine.aggregator.clusterConfig: ABC

完成這些步驟后,我們還需要調整下服務提供者,我們需要兩個服務提供者同時運行。

將8765(SERVICE-FEIGN)的服務提供者改為8766,名稱改為:SERVICE-FEIGN-2,運行起來,別忘了在運行設置中設置允許多個實例運行。修改后的SERVICE-FEIGN的yml是:

server:
  # 服務端口號
  port: 8766
spring:
  application:
    # 服務名,即serviceId
    name: service-feign-2
eureka:
  client:
    serviceUrl:
      # 安全認證的服務注冊中心地址
      defaultZone: http://localhost:8761/eureka
# 熔斷器設置
feign:
  hystrix:
    enabled: true
# 熔斷器DashBoard: actuator在boot2.0調整后開關web端點的配置,*代表開啟所有
management:
  endpoints:
    web:
      exposure:
        include: "*"

運行后結構如圖:

在這里插入圖片描述

我們訪問:

訪問 http://localhost:8767/turbine.stream

可以看到ping的信息流,這時說明8767正在不斷ping指定的兩個服務

在這里插入圖片描述

進行圖形化監(jiān)控查看,輸入:http://localhost:8767/hystrix ,返回酷酷的小熊界面,輸入: http://localhost:8767/turbine.stream ,然后點擊 Monitor Stream ,可以看到出現了監(jiān)控列表:

在這里插入圖片描述

咦?為啥只有一個監(jiān)控呢,說好的聚合監(jiān)控列表呢。

這里有個坑,或者說是理解上的誤區(qū):

監(jiān)控不同的服務熔斷,首先得是不同的rpc調用,也就是消費者的熔斷函數要是兩個不同的,或者,消費者去調用的是兩個不同的服務提供者!這樣才會有多個監(jiān)控表。不然,有何意義?

想明白這一步,你就應該知道怎么把多個表弄出來了。

你需要讓消費者有一個:

  • 新的消費目標(新的服務提供者)
  • 或者是一個新的消費熔斷器。

修改server-feign子模塊

  1. ServiceHi接口的調用變?yōu)閟ayHiFromServiceHi2,value也指向另一個服務提供者service-hi-2 (新的消費目標(新的服務提供者))
@FeignClient(value = "service-hi-2", fallback = ServiceHiHystrix.class)
public interface ServiceHi {
    /**
     * <p>通過Feign偽Http客戶端調用service-hi提供的服務</p>
     * @author hanchao 2018/5/19 17:59
     **/
    @GetMapping("/hi/{name}")
    String sayHiFromServiceHi2(@PathVariable(value = "name") String name);
}

  1. 修改熔斷器ServiceHiHystrix (新的消費熔斷器)
@Component
public class ServiceHiHystrix implements ServiceHi {
    @Override
    public String sayHiFromServiceHi2(String name) {
        return "hello" + name +", this message send failed. By Hystrix.";
    }
}
  1. 當然,隨之更改的是HelloController,需要改為return serviceHi.sayHiFromServiceHi2
@RestController
public class HelloController {

    /** 注入服務"service-hi"的Feign客戶端ServiceHi */
    @Autowired
    private ServiceHi serviceHi;

    /**
     * 調用Feign客戶端提供的服務,自帶負載均衡
     * @param name
     * @return
     */
    @GetMapping("/hello/{name}")
    public String sayHi(@PathVariable String name){
        //調用Feign客戶端ScheduleServiceHi的接口
        return serviceHi.sayHiFromServiceHi2(name);
    }
}

這樣改完后,重啟這個8766的服務消費者

結構依然不變:

在這里插入圖片描述

我們得到了:

在這里插入圖片描述
  • 上方兩排:不同的熔斷器
  • 下方兩排:不同的消費提供者

這里確實比較難理解哈,坑了我好久。

參考

springcloud(四):熔斷器Hystrix

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

springcloud(五):熔斷監(jiān)控Hystrix Dashboard和Turbine

http://www.ityouknow.com/springcloud/2017/05/18/hystrix-dashboard-turbine.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容