天天看點

SpringCloud淺嘗(八)——Hystrix

Hystrix 具備服務降級、服務容錯、服務熔斷、線程和信号隔離、請求緩存、請求合并以及服務監控等強大功能。

多層服務調用中,較低級别的服務中的服務故障可能導緻使用者級聯故障。當對特定服務的呼叫達到一定門檻值時(Hystrix中的預設值為5秒内的20次故障),電路打開,不進行通話。在錯誤和開路的情況下,開發人員可以提供後備。

下圖來自官方網站

SpringCloud淺嘗(八)——Hystrix

Hystrix特性

  1.請求熔斷: 當Hystrix Command請求後端服務失敗數量超過一定比例(預設50%), 斷路器會切換到開路狀态(Open). 這時所有請求會直接失敗而不會發送到後端服務. 斷路器保持在開路狀态一段時間後(預設5秒), 自動切換到半開路狀态(HALF-OPEN).這時會判斷下一次請求的傳回情況, 如果請求成功, 斷路器切回閉路狀态(CLOSED), 否則重新切換到開路狀态(OPEN). 

  2.服務降級:Fallback相當于是降級操作. 對于查詢操作, 我們可以實作一個fallback方法, 當請求後端服務出現異常的時候, 可以使用fallback方法傳回的值. fallback方法的傳回值一般是設定的預設值或者來自緩存.告知後面的請求服務不可用了,不要再來了。

  3.依賴隔離(采用艙壁模式,Docker就是艙壁模式的一種):在Hystrix中, 主要通過線程池來實作資源隔離. 通常在使用的時候我們會根據調用的遠端服務劃分出多個線程池.比如說,一個服務調用兩外兩個服務,你如果調用兩個服務都用一個線程池,那麼如果一個服務卡在哪裡,資源沒被釋放。後面的請求又來了,導緻後面的請求都卡在哪裡等待,導緻你依賴的A服務把你卡在哪裡,耗盡了資源,也導緻了你另外一個B服務也不可用了。這時如果依賴隔離,某一個服務調用A B兩個服務,如果這時我有100個線程可用,我給A服務配置設定50個,給B服務配置設定50個,這樣就算A服務挂了,我的B服務依然可以用。

  4.請求緩存:比如一個請求過來請求使用者的資料,你後面的請求也過來請求同一個使用者的資料,這時我不會繼續走原來的那條請求鍊路了,而是把第一次請求緩存過了,把第一次的請求結果傳回給後面的請求。

  5.請求合并:我依賴于某一個服務,我要調用N次,比如說查資料庫的時候,我發了N條請求發了N條SQL然後拿到一堆結果,這時候我們可以把多個請求合并成一個請求,發送一個查詢多條資料的SQL的請求,這樣我們隻需查詢一次資料庫,提升了效率。

我們前面EurekaDiscovery2執行個體中,使用了Feign來調用EurekaDiscovery中的接口,Feign内部已包含了hystrix。下面我們先使用Feign來實作降級融斷。

在EurekaDiscovery2配置中添加

feign:
  hystrix:
    enabled: true  # 開啟hystrix
           

我們添加一個Fallback類并實作HelloRemote接口

@Component
public class HelloFallback implements HelloRemote {
    @Override
    public String home(@RequestParam("name") String name) {
        return name+"現在太擁擠了, 請稍後重試~-Feign";
    }
}
           

我們指定fallback類為HelloFallback 

@FeignClient(name= "eurekadiscovery", fallback = HelloFallback.class)
public interface HelloRemote {
    @GetMapping("/hi")
    public String home(@RequestParam("name") String name);
}
           

下面我們啟動執行個體來驗證一下

正常調用:

SpringCloud淺嘗(八)——Hystrix

我們模拟EurekaDiscovery挂掉(關閉),觸發了服務降級

SpringCloud淺嘗(八)——Hystrix

逾時導緻服務降級,預設feign調用逾時是1000毫秒,我們EurekaDiscovery中将線程sleep1000豪秒,同樣出現了上面一樣的服務降級。

我們可以通下面的配置來設定逾時時間

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
           

通過調整hystrix的逾時時間,服務能正常調用

SpringCloud淺嘗(八)——Hystrix

當我們沒有使用Feign時也可直接引用Hystrix元件來實作在,我們在項目中引入spring-cloud-starter-netflix-hystrix

啟動類中我們添加注解@EnableCircuitBreaker 允許斷路器

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class EurekaDiscovery2Application {

	@Bean
	@LoadBalanced
	RestTemplate restTemplate(){
		return new RestTemplate();
	}

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

}
           

我們通過@HystrixCommand

@HystrixCommand(fallbackMethod = "fallback")
    public String home(@RequestParam("name") String name)
    {
        return restTemplate.getForEntity("http://EUREKADISCOVERY/hi?name="+name,String.class).getBody();
    }

     public String fallback(String name) {
        return name+",現在太擁擠了, 請稍後重試~";
    }
           

我們啟動執行個體,能正常調用

SpringCloud淺嘗(八)——Hystrix

我們模拟EurekaDiscovery挂掉(關閉),觸發了服務降級

SpringCloud淺嘗(八)——Hystrix

我們還可以通過@DefaultProperties(defaultFallback = "defaultFallback"),來指定預設的FallBack函數,這樣就不需要每個 @HystrixCommand 注解都指定一個FallBack函數了

@RestController
@DefaultProperties(defaultFallback = "defaultFallback")
public class HelloController {
    @Autowired
    private  RestTemplate restTemplate;


    @GetMapping("/hi")
    @HystrixCommand
    // @HystrixCommand(fallbackMethod = "fallback")
    public String home(@RequestParam("name") String name)
    {
        return restTemplate.getForEntity("http://EUREKADISCOVERY/hi?name="+name,String.class).getBody();
    }

    public String fallback(String name) {
        return name+",現在太擁擠了, 請稍後重試~";
    }

    public String defaultFallback()
    {
        return "現在太擁擠了, 請稍後重試~";
    }
           

這樣我們降級後就會調用預設的FallBack函數

SpringCloud淺嘗(八)——Hystrix

我們還可以通過@HystrixCommand來設定逾時時間,我們一般都是采用在配置檔案中設定逾時間時間。

@HystrixCommand(commandProperties = {
            // 設定逾時時間為3秒
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
           
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
    home:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000
           

繼續閱讀