白話SpringCloud | 第五章:服務容錯保護(Hystrix)

前言

前一章節,咱們知道了如何利用RestTemplate+RibbonFeign的方式進行服務的調用。在微服務架構中,一個服務可能會調用不少的其餘微服務應用,雖然作了多集羣部署,但可能還會存在諸如網絡緣由或者服務提供者自身處理的緣由,或多或少都會出現請求失敗或者請求延遲問題,若服務提供者長期未對請求作出迴應,服務消費者又不斷的請求下,可能就會形成服務提供者服務崩潰,進而服務消費者也一塊兒跟着不可用,嚴重的時候就發生了系統雪崩了。鑑於此,產生了斷路器等一系列的服務保護機制。本章節,就來講下如何利用Hystrix進行容錯處理。html

一點知識

按照此係列的慣例,咱們先來了解下一些相關的知識。java

注:如下部份內容轉至大佬純潔的微笑:熔斷器Hystrixgit

容錯處理手段

容錯處理是指軟件運行時,能對由非正常因素引發的運行錯誤給出適當的處理或信息提示,使軟件運行正常結束——百度百科程序員

從百度百科的解釋中能夠看出,簡單理解,所謂的容錯處理其實就是捕獲異常了,不讓異常影響系統的正常運行,正如java中的try catch同樣。github

而在微服務調用中,自身異常可自行處理外,對於依賴的服務若發生錯誤,或者調用異常,或者調用時間過長等緣由時,避免長時間等待,形成系統資源耗盡。 通常上都會經過設置請求的超時時間,如http請求中的ConnectTimeoutReadTimeout;再或者就是使用熔斷器模式,隔離問題服務,防止級聯錯誤等。spring

雪崩效應

在微服務架構中,存在不少的微服務單元,各個微服務之間經過網絡進行通信,不免出現依賴關係,若某一個單元出現故障,就很容易因依賴關係而引起故障的蔓延,產生「雪崩效應」,最終致使整個系統的癱瘓。設計模式

下面這張圖,相比你們都有看過了。瀏覽器

雪崩效應

如圖所示:A做爲服務提供者,B爲A的服務消費者,C和D是B的服務消費者。A不可用引發了B的不可用,並將不可用像滾雪球同樣放大到C和D時,雪崩效應就造成了。也就應了那句話:星星之火,能夠燎原!服務器

熔斷器

熔斷器,和現實生活中的空氣開關做用很像。它能夠實現快速失敗,若是它在一段時間內偵測到許多相似的錯誤,會強迫其之後的多個調用快速失敗,再也不訪問遠程服務器,從而防止應用程序不斷地嘗試執行可能會失敗的操做,使得應用程序繼續執行而不用等待修正錯誤,或者浪費CPU時間去等到長時間的超時產生。熔斷器也可使應用程序可以診斷錯誤是否已經修正,若是已經修正,應用程序會再次嘗試調用操做。微信

熔斷器模式就像是那些容易致使錯誤的操做的一種代理。這種代理可以記錄最近調用發生錯誤的次數,而後決定使用容許操做繼續,或者當即返回錯誤

熔斷器狀態轉換圖

能夠看出,熔斷器一共有三種狀態,之間轉換關係以下:

  • 關閉狀態 當熔斷器處於關閉狀態時,請求是能夠被放行的; 當熔斷器統計的失敗次數觸發開關時,轉爲打開狀態。
  • 打開狀態 當熔斷器處於打開狀態時,全部請求都是不被放行的,直接返回失敗; 只有在通過一個設定的時間窗口週期後,熔斷器纔會轉換到半開狀態
  • 半開狀態 當熔斷器處於半開狀態時,當前只能有一個請求被放行; 這個被放行的請求得到遠端服務的響應後,假如是成功的,熔斷器轉換爲關閉狀態,不然轉換到打開狀態。

我的以爲,主要仍是快速失敗,避免請求堆積,壓垮服務器。進而起到保護服務高可用的目的。

Hystrix實踐

何爲Hystrix

Hystrix是一個實現了超時機制和斷路器模式的工具類庫。

Hystrix是有Netflix開源的一個延遲和容錯庫,用於隔離訪問遠程系統、服務或第三方庫,防止級聯失敗,從而提高系統的可用性和容錯性。

Hystrix容錯機制:

  • 包裹請求:使用HystrixCommand包裹對依賴的調用邏輯,每一個命令在獨立線程中執行,這是用到了設計模式「命令模式」。
  • 跳閘機制:當某服務的錯誤率超過必定閾值時,Hystrix能夠自動或手動跳閘,中止請求該服務一段時間。
  • 資源隔離:Hystrix爲每一個依賴都維護了一個小型的線程池,若是該線程池已滿,發往該依賴的請求就被當即拒絕,而不是排隊等候,從而加速斷定失敗。
  • 監控:Hystrix能夠近乎實時的監控運行指標和配置的變化。如成功、失敗、超時、被拒絕的請求等。
  • 回退機制:當請求失敗、超時、被拒絕,或當斷路器打開時,執行回退邏輯。回退邏輯可自定義。
  • 自我修復:斷路器打開一段時間後,會自動進入半開狀態,斷路器打開、關閉、半開的邏輯轉換。

下圖就是Hystrix的回退策略,防止級聯故障。

Hystrix fallback prevents cascading failures

常規方式整合Hystrix

建立個工程spring-cloud-hystrix工程。 0.引入POM依賴。

<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>

1.啓動類,加入註解@EnableHystrix,同時申明一個實現負載均衡的RestTemplate。(關於消費者服務可查看:第四章:服務消費者(RestTemple+Ribbon+Feign),這裏再也不闡述了。)

/**
 * 熔斷器示例
 * @author oKong
 *
 */
@SpringBootApplication
@EnableHystrix
@EnableDiscoveryClient
@Slf4j
public class HystrixApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(HystrixApplication.class, args);
        log.info("sprign-cloud-hystrix啓動!");
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplat() {
        return new RestTemplate();
    }
    
}

2.編寫一個測試類,加入@HystrixCommand,指定fallbackMethod方法。

RibbonController.java

/**
 * ribbon 常規方式-示例
 * @author oKong
 *
 */
@RestController
@Slf4j
public class RibbonController {

    @Autowired
    RestTemplate restTemplate;
    
    @GetMapping("/ribbon")
    @HystrixCommand(fallbackMethod="fallback")
    public String hello(String name) {
        log.info("使用restTemplate調用服務,參數name:{}", name);
        return restTemplate.getForObject("http://eureka-client/hello?name=" + name, String.class);
    }
    
    /**
     * 發生熔斷時調用的方法
     * @param name
     * @param throwable 發生異常時的異常信息
     * @return
     */
    public String fallback(String name,Throwable throwable) {
        log.error("熔斷髮生了:{}", throwable);
        log.warn("restTemplate調用服務發生熔斷,參數name:{}", name);
        return "restTemplate調用服務發生熔斷,參數name:" + name;
    }
}

注意:這裏fallback方法加入了一個參數throwable,當發生熔斷時,能夠獲悉發生熔斷的異常信息,便於定位問題和緣由。

3.啓動應用,訪問:http://127.0.0.1:8038/ribbon?name=oKong 。正常狀況下,spring-cloud-eureka-client應用正常運行時,返回正常結果:

正常狀況

如今咱們中止提供者服務,再次訪問,能夠看見已經進入熔斷方法了:

異常狀況

控制檯能夠看見異常輸出:

異常信息

因爲實例還沒有被剔除註冊中心的服務列表,因此提示是鏈接超時,等待一段時間後,再次訪問服務,能夠看見是提示實例不存在了:

No instances available for eureka-client

注意:對於@HystrixCommand註解,咱們能夠放在任何一個調用函數裏面,以此實現調用方法發生異常或者錯誤時,能夠快速返回,避免持續請求,形成資源的耗盡。

Feign整合Hystrix

如上小節說示例的,當咱們方法不少時,要是分別編寫一個fallback估計也是崩潰的,雖然可使用一個通用的fallback,但未進行特殊設置下,也是沒法知道具體是哪一個方法發生熔斷的。

而對於Feign,咱們可使用一種更加優雅的形式進行。咱們能夠指定@FeignClient註解的fallback屬性,或者是fallbackFactory屬性,後者能夠獲取異常信息的。

修改spring-cloud-hystrix工程。

0.引入Feigin的POM依賴。

<!-- feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

1.啓動類,加入@EnableFeignClients啓用Feign.

**
 * 熔斷器示例
 * @author oKong
 *
 */
@SpringBootApplication
@EnableHystrix
@EnableDiscoveryClient
@EnableFeignClients
@Slf4j
public class HystrixApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(HystrixApplication.class, args);
        log.info("sprign-cloud-hystrix啓動!");
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplat() {
        return new RestTemplate();
    }
    
}
  1. 建立一個服務接口類IHelloClient.java,同時定義fallback或者fallbackFactory屬性值。注意:二者 同時設置時,優先調用fallbackfallbackFactory不進行調用了。
@FeignClient(name="eureka-client",/*fallback=HelloClientFailImpl.class,*/ fallbackFactory = HelloClientFallbackFactory.class)
public interface IHelloClient {

    /**
     * 定義接口
     * @param name
     * @return
     */
    @RequestMapping(value="/hello", method=RequestMethod.GET)
    public String hello(@RequestParam("name") String name);
}
  1. 建立fallbackfallbackFactory屬性對應類。

HelloClientFailImpl.java

@Component("fallback")
@Slf4j
public class HelloClientFailImpl implements IHelloClient{
    
    @Override
    public String hello(String name) {
        log.error("restTemplate調用[hello]服務發生熔斷,參數name:{}", name);
        return "restTemplate調用[hello]服務發生熔斷,參數name:" + name;
    }
}

HelloClientFallbackFactory/java

@Component
@Slf4j
public class HelloClientFallbackFactory implements FallbackFactory<IHelloClient>{

    @Autowired
    @Qualifier("fallback")
    IHelloClient helloClient;
    
    @Override
    public IHelloClient create(Throwable cause) {
        log.error("feign調用發生異常,觸發熔斷", cause);
        return helloClient;
    }

}

能夠知道,正常fallback就是一個接口的實現類,當發送異常時,會調用此接口實現類進行服務調用。而FallbackFactory是也是一個接口實現類,須要實現feign.hystrix.FallbackFactory<T>接口,在發生熔斷時,調用create方法,同時返回被調用接口的實現類,以便進行fallback處理。

3.配置文件開啓feign的熔斷器功能。

feign.hystrix.enabled=true

或者,申明一個Feign.Builder類也是能夠的,咱們從org.springframework.cloud.openfeign.FeignClientsConfiguration能夠看出,啓用feign的條件:

HystrixFeignConfiguration

因此正常,咱們只須要在配置文件中加入feign.hystrix.enabledtrue便可,注意:此屬性在IDE下未進行提示的。 或者就如此類同樣,申明一個bean:

@Bean
public Feign.Builder feignHystrixBuilder() {
            return HystrixFeign.builder();
        }

也是能夠的。

4.編寫一個測試類FeignController

/**
 * feign 熔斷器示例
 * @author oKong
 *
 */
@RestController
@Slf4j
public class FeignController {

    @Autowired
    IHelloClient helloClient;
    
    @GetMapping("/feign")
    public String hello(String name) {
        log.info("使用feign調用服務,參數name:{}", name);
        return helloClient.hello(name);
    }
    
}

5.再次啓動應用,訪問:http://127.0.0.1:8038/feign?name=oKong ,正常調用以下:

正常調用

關閉服務提供者,再次訪問,瀏覽器返回了錯誤提示:

fallback

同時,咱們使用了FallbackFactory,控制檯打印出了具體異常:

異常信息


針對熔斷超時時間等相關設置,能夠經過@HystrixCommand註解的各屬性進行配置,主要仍是commandProperties屬性值,具體的參數可查看com.netflix.hystrix.HystrixCommandProperties類,也能夠針對某個調用方法進行特殊設置。具體的能夠看看這篇文章:hystrix的基本介紹和配置屬性說明,或者能夠去大佬程序員DD博客查閱下關於 Hystrix相關知識點:服務容錯保護(Hystrix斷路器)【Dalston版】服務容錯保護(Hystrix依賴隔離)【Dalston版】,版本雖然是D版的,但原理是差很少的~


參考資料

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

  2. https://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#_circuit_breaker_hystrix_clients

總結

本章節主要講解了如何整合Hystrix。自己Hystrix已經包含了服務降級依賴隔離熔斷器等功能了,咱們使用時並沒進行特殊設置,默認都是生效的。對於一些關於Hystrix的高級用法,好比信號量隔離、線程池的設置等等,還有一些像超時時間等,因爲此方面瞭解的很少,這裏就不班門弄斧了。你們可去官方網站或者谷歌搜索下相關材料下,對於一些業務場景,可進行一些自定義設置。主要仍是針對@HystrixCommand註解的相關配置。關於調用統一異常的處理相關實踐,好比當提供方異常時,調用方如何進行統一異常處理,或者服務不可用時,進行統一的異常捕獲,告知外圍調用者服務不可用等信息。這些相關實踐部分會在以後的實踐篇系列文章中進行闡述的,也許不是最佳的實踐,僅但願提供一個參考方案吧。這裏就不表了,敬請期待~

最後

目前互聯網上大佬都有分享SpringCloud系列教程,內容可能會相似,望多多包涵了。原創不易,碼字不易,還但願你們多多支持。若文中有錯誤之處,還望提出,謝謝。

老生常談

  • 我的QQ:499452441
  • 微信公衆號:lqdevOps

公衆號

我的博客:http://blog.lqdev.cn

源碼示例:https://github.com/xie19900123/spring-cloud-learning

原文地址:http://blog.lqdev.cn/2018/09/23/SpringCloud/chapter-five/

相關文章
相關標籤/搜索