1. 程式人生 > >spring cloud深入學習(五)-----熔斷器Hystrix

spring cloud深入學習(五)-----熔斷器Hystrix

簡單 -h 線程資源 不能 oca ali contex 並且 com

什麽是Hystrix?

在分布式系統中,服務與服務之間的依賴錯綜復雜,一種不可避免的情況就是某些服務會出現故障,導致依賴於它們的其他服務出現遠程調度的線程阻塞。Hystrix提供了熔斷器功能,能夠阻止分布式系統中出現聯動故障,通過隔離服務的訪問點阻止聯動故障的,並提供了故障的解決方案,從而提高 了整個分布式系統的彈性。

簡單來說:微服務架構中存在很多的服務,例如A、B、C、D、E5個服務,並且對於一次調用來說,並不僅僅是訪問一個服務,按照功能劃分來說,每個服務做得事情是單一的,所以大部分的請求都是類似於A-C-E,跨過多個服務。

如果單個服務的正常運行幾率為99.00%(機房的不可靠性、運營商的不可靠性等等),那麽對於A-C-E調用流程來說,幾率為99.00%*99.00%*99.00%=97.03%,也就意味著10000次請求有300多次會失敗,問題還是比較嚴重的。

Hystrix設計原則

  • 防止單個服務的故障耗盡整個服務的Servlet容器(例如Tomcat)的線程資源。
  • 快速失敗機制,如果某個服務出現了故障,則調用該服務的請求快速失敗,而不是線程等待。
  • 提供回退(fallback)方案,在請求發生故障時,提供設定好的回退方案。
  • 使用熔斷機制,防止故障擴散到其他服務。
  • 提供熔斷器的監控組件Hystrix Dashboard ,可以實時監控熔斷器的狀態。

Hystrix工作機制

  • 當服務的某個 API 接口的失敗次數在一定時間內小於設定的閥值時,熔斷器處於關閉狀態,該 API 接口正常提供服務;
  • 當該API 接口處理請求的失敗次數大於設定的閥值時, Hystrix 判定該 API 接口出現了故障,打開熔斷器,這時請求該 API接口會執行快速失敗的邏輯(即 fall back 回退的邏輯),不執行業務邏輯,請求的線程不會處於阻塞狀態;
  • 處於打開狀態的熔斷器一段時間後會處於半打開狀態,並將一定數量的請求執行正常邏輯。剩余的請求會執行快速失敗,若執行正常邏輯的請求失敗了,則熔斷器繼續打開;若成功了 ,則將熔斷器關閉。這樣熔斷器就具有了自我修復的能力。

使用方法

1、在RestTemplate和Ribbon上使用熔斷器

找到本人之前的eureka-consumer項目,gitbub地址:https://github.com/ali-mayun/eureka-consumer

新加依賴:

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

運行主類:

package com.ty.eurekaconsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

@EnableEurekaClient
@SpringBootApplication
//使用這註解開啟hystrix的功能
@EnableHystrix
public class EurekaConsumerApplication {

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

}

更改之前的service

package com.ty.eurekaconsumer.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.client.RestTemplate;

@Service
public class RibbonService {

    @Autowired
    private RestTemplate restTemplate;

    //使用@HystrixCommand註解啟用熔斷器的功能,當服務熔斷後,將不調用遠程服務,直接調用本地的handleError方法
    @HystrixCommand(fallbackMethod = "handleError")
    public String hiService(String name) {
        return restTemplate.getForObject("http://eureka-provider/firstCall?name=" + name, String.class);
    }

    public String handleError(String name) {
        return name + "在訪問遠程服務時,被熔斷了,調用本地的方法";
    }
}

現在啟動eureka-server、eureka-consumer兩個項目,不啟動eureka-provider,制造服務不能用的假象,運行效果如下:

技術分享圖片

2、在feign上使用熔斷器

上面一種方式相對來說還是比較麻煩的,使用feign則方便很多。

打開上篇博文中的eureka-feign-consumer項目,github地址:https://github.com/ali-mayun/eureka-feign-consumer

修改application.properties文件,開啟Hystrix功能:

# 服務名稱
spring.application.name=eureka-feign-consumer
# 端口號
server.port=4001
# 服務註冊中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/
# 增加Hystrix功能
feign.hystrix.enabled=true

更改EurekaClientFeign:

package com.ty.eurekafeignconsumer.service;

import com.ty.eurekafeignconsumer.config.FeignConfig;
import com.ty.eurekafeignconsumer.hystrix.BaseHystrix;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

//新增fallback屬性指定出現熔斷由哪個類來處理。
@FeignClient(value = "eureka-provider", configuration = FeignConfig.class, fallback = BaseHystrix.class)
public interface EurekaClientFeign {

    //只需要在接口中定義方法即可。調用eureka-provider服務的firstCall方法,並且feign集成了Ribbon
    @GetMapping(value = "/firstCall")
    String sayHiFromClientEureka(@RequestParam("name") String name);
}

BaseHystrix:

package com.ty.eurekafeignconsumer.hystrix;

import com.ty.eurekafeignconsumer.service.EurekaClientFeign;
import org.springframework.stereotype.Component;

//熔斷處理類需要實現調用遠程服務的service,例如本案例在EurekaClientFeign類中調用遠程服務,就需要實現此接口,
//然後相同名字的方法就可以一一對應
@Component(value = "baseHystrix")
public class BaseHystrix implements EurekaClientFeign {
    @Override
    public String sayHiFromClientEureka(String name) {
        return name + "訪問的遠程服務被熔斷";
    }
}

controller中有個坑,就是EurekaClientFeign是一個接口,主類中使用@EnableFeignClients開啟了feign功能,該註解會掃描@FeignClient,將使用到該註解的接口通過動態代理的方式創建類,因此EurekaClientFeign接口就會有兩個實現類。

另一個是BaseHystrix,用來處理發生熔斷後的邏輯,因此這裏使用@Autowired就出錯了,但是使用@Resource就ok,但是暫不清楚為啥會正確的找到動態代理生成的那個類,後續研究。。。

package com.ty.eurekafeignconsumer.controller;

import com.ty.eurekafeignconsumer.service.EurekaClientFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class FeignController {

    //這個地方有點坑,因為EurekaClientFeign已經有另外一個實現類就是BaseHystrix,使用@Autowired會報錯
    //不過暫時不清楚@Resource會註入正確的bean,後續待研究。。。
    @Resource
    private EurekaClientFeign eurekaClientFeign;

    @GetMapping("/hi")
    public String sayHi(@RequestParam(value = "name", defaultValue = "馬雲", required = false) String name) {

        return eurekaClientFeign.sayHiFromClientEureka(name);
    }
}

當eureka-provider正常開啟,效果如下:

技術分享圖片

關閉eureka-provider,訪問效果如下:

技術分享圖片

spring cloud深入學習(五)-----熔斷器Hystrix