断路器Hystrix

Posted Lmg12580

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了断路器Hystrix相关的知识,希望对你有一定的参考价值。

分布式系统面临的问题

    复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。

服务雪崩

    多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“ 扇出 ”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,从而引起系统崩溃,这就是所谓的“雪崩效应”。
Hystrix是什么?
    Hystrix是一个用于处理分布式系统的 延迟 容错 的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,它能够保证在一个依赖出问题的情况下, 不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

    “断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack)而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

Hystrix能干什么?

服务降级、服务熔断、服务限流、服务监控

    题外话:hystrix直译为豪猪,是一种背上长满刺的动物,跟刺猬一样,也不知道老外为啥取了这么个名字。同属于Netflix公司下的产品,一样的优秀,目前也停更进维,国内使用还是比较多的,所以也得学。但是目前阿里推出了sentinel,流行趋势很大,估计以后都要换了,在后续推出的Cloud Alibaba篇会详细介绍。

Hystrix几个重要概念
1. 服务降级
    不让客户端等待并立刻返回一个友好提示,如服务器繁忙,请稍后再试。
触发降级的场景:
  • 程序运行异常

  • 超时

  • 服务熔断触发服务降级

  • 线程池/信号量打满

2. 服务熔断
     熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
     当检测到该节点微服务调用响应正常后,恢复调用链路。
服务的降级->进而熔断->恢复调用链路
3. 服务限流

    秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。

Hystrix既可以放在消费端,也可以放在服务提供端,没有严格意义上的要求,但是一般工作中放在消费端的比较多。为了学习,下面在消费方和服务方都配一下。


一、新建两个工程

服务提供者:cloud-provider-hystrix-payment

1. 服务提供者pom文件添加Hystrix的依赖包

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

2. 修改yml文件如下

server: port: 8001spring: application:    name: cloud-provider-hystrix-paymenteureka: client:    register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka8761.com:8761/eureka

3. 新增PaymentService.java类,添加方法

@Servicepublic class PaymentService { public String paymentInfoTimeOut(Integer id) { try { Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } return "线程池:" + Thread.currentThread().getName() + " paymentInfoTimeOut,id:" + id + " :)"; }}

4. 新增PaymentController.java类,添加方法

@RestControllerpublic class PaymentController { @Resource  private PaymentService paymentService;   @GetMapping("/payment/hystrix/timeout/{id}") public String paymentInfoTimeOut(@PathVariable("id") Integer id){ return paymentService.paymentInfoTimeOut(id); }}
服务消费者:cloud-consumer-feign-hystrix-order

1. pom文件添加Hystrix依赖,同上

2. 修改yml配置

server:  port: 80eureka: client: register-with-eureka: false service-url:      defaultZone: http://eureka8761.com:8761/eurekaribbon: ReadTimeout: 5000 #指的是建立连接所需要的时间 ConnectTimeout: 5000 #指的是建立连接后从服务器读取到可用资源所用的时间

3. 主启动类

@SpringBootApplication@EnableFeignClientspublic class OrderHystrixApplication { public static void main(String[] args) { SpringApplication.run(OrderHystrixApplication.class, args); }}

4. 新增PaymentHystrixService.java接口,用来远程调用支付服务

@Component@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")public interface PaymentHystrixService { @GetMapping("/payment/hystrix/timeout/{id}") String paymentInfoTimeOut(@PathVariable("id") Integer id);}

5. 新增OrderHystrixController.java类

@RestControllerpublic class OrderHystrixController { @Resource private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/hystrix/timeout/{id}") public String paymentInfoTimeOut(@PathVariable("id") Integer id){ return paymentHystrixService.paymentInfoTimeOut(id); }}

以上两个工程搭建完以后,启动eureka及这两个服务,浏览器测试能否调通,如果出现如下内容则搭建成功(为了方便测试,后续将eureka集群的配置去掉,使用单节点的配置)。

二、服务降级
    在什么场景下使用服务降级前面已经介绍过了,也比较容易理解,这里就不演示了,下面直接介绍如何使用服务降级。
  • 在服务提供者端使用

服务端使用服务降级的目的,主要是为了确保提供方的接口无误,只有上游的接口正确,才能保证链路的稳定性。服务降级核心注解@HystrixCommand

1. 修改service方法,在要加服务降级的方法上添加@HystrixCommand注解,注解内指定在调用当前方法失败后的fallback方法,该方法需要我们手动编码实现,同时给接口设置1秒的超时时间,意思就是如果调用此接口超过1秒,则会启用服务降级。

断路器Hystrix

2. 主启动类添加注解:

@EnableCircuitBreaker

我们使用Hystrix的话,需要在主启动类添加该注解启用功相关功能。

断路器Hystrix

4. 可以发现已经成功走到了fallback方法,原因是我们在方法内设置了3秒的超时时间,但是接口调用要求不能超过1秒,这时候他就启用了服务降级。

    还有一个小细节就是我们注意这时候他的工作线程是Hystrix内置的,已经不是tomcat的线程了。

  • 在消费端使用
1. 消费端使用其实也一样,参照服务端,修改controller方法,超时时间设为2秒,消费端的超时改为5秒,这样做的目的是为了模拟消费端超时异常,做服务降级。

断路器Hystrix

2. 改完后主启动类添加启用注解(别忘了),启动消费端,浏览器测试,可以发现消费端超过两秒后自动走到了fallback方法。

断路器Hystrix

     做到这里,我们思考一个问题,如果我们项目里有100个方法需要做服务降级,我们不可能写100个fallback,太傻了,所以Hystrix提供了一个设置全局的降级方法。
  • 全局服务降级
核心注解 @DefaultProperties

1. 在消费端controller类上面加此注解

断路器Hystrix

断路器Hystrix

    这个做完以后,还有一个问题,我们的代码都写在一个类里,不仅看起来乱,耦合性也比较高,降级和业务逻辑混在一起,所以我们需要进一步优化。
  • 通配服务降级FeignFallback

1. 重新新建一个类,实现feign接口,统一为接口里的方法进行异常处理。
@Componentpublic class PaymentFallbackService implements PaymentHystrixService{ @Override public String paymentInfoTimeOut(Integer id) { return "PaymentFallbackService fallback"; }}

2. yml文件添加以下内容

#用于服务降级在注解@FeignClient中添加fallbackFactory属性值feign: hystrix: enabled: true

3. feign接口中指定fallback类

@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)

断路器Hystrix

三、服务熔断
     熔断的概念看上面,当开启服务熔断后,Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值(缺省是5秒内20次调用失败),就会启动熔断机制。

1. 修改服务提供者8001,添加以下代码,重点是注解内的四个参数,这个配置的意思就是说,在10秒内请求10次,如果有6次失败,则打开断路器

    PaymentService.java

@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback",commandProperties = { @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//是否开启断路器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),//请求次数      @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//时间窗口期,时间间隔 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")//失败率达到多少后跳闸 }) public String paymentCircuitBreaker(Integer id){ if (id < 0){ throw new RuntimeException("id不能为负数"); } String serialNumber = IdUtil.simpleUUID(); return Thread.currentThread().getName()+ "	"+"调用成功,流水号:"+serialNumber; } public String paymentCircuitBreakerFallback(Integer id){ return "id不能为负数,请重试~ id:"+id; }

    PaymentController.java

@GetMapping("/payment/circuit/{id}")public String paymentCircuitBreaker(@PathVariable("id") Integer id){ return paymentService.paymentCircuitBreaker(id);}
2. 重启8001服务,在浏览器输入地址测试

a. id先输入正数,正常返回

断路器Hystrix

b. id输入负数,报错

断路器Hystrix

c. id输入负数,疯狂加载几次,再输入正数,报错。这时“1”明明正数,却报错,说明这时候断路器被打开了,我们走的是fallback方法

断路器Hystrix

d. id输入正数,多加载几次,慢慢的就正常返回

断路器Hystrix

总结:

熔断打开请求不再进行调用当前微服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态。

熔断关闭熔断关闭不会对服务进行熔断。

熔断半开 部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断。

三、服务限流

    Hystrix的限流就不学了,到了后面Alibaba篇学阿里的限流。

四、服务监控Dashboard

    Hystrix提供了准实时的调用监控,它会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。 
1. 新建一个工程,专门用来做服务监控
    工程名:cloud-consumer-hystrix-dashboard

2. 添加pom依赖

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

3. 修改yml文件

server: port: 9001hystrix: dashboard:    proxy-stream-allow-list: "*"

4. 主启动类添加开启注解

@SpringBootApplication@EnableHystrixDashboardpublic class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); }}

http://localhost:9001/hystrix

断路器Hystrix

注意事项:

a. 如果需要监控服务提供类,则被监控的服务必须有以下依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId></dependency>

b. 被监控服务主启动类添加如下代码,此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑

@Beanpublic ServletRegistrationBean getServlet(){ HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean;}
6. 服务端修改完后重启,在dashboard面板输入监控地址

7. 请求一下8001的熔断接口,就上面测得正负数的那个,然后点击Monitor Stream,进入仪表盘

     实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。

    该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压力实例。

tips:dashboard可以同时监控多个服务,所有服务都会在这里展示出来。


到这里,Hystrix的基本知识就学习完了


接下来开始学习下个组件:新一代路由网关GateWay


以上是关于断路器Hystrix的主要内容,如果未能解决你的问题,请参考以下文章

springCloud Finchley 微服务架构从入门到精通断路器 Hystrix(feign)

12Feign整合断路器Hystrix

Hystrix断路器

微服务断路器模式实现:Istio vs Hystrix

第四篇 断路器(Hystrix) --IDEA SpringCloud全攻略 亲测可用

spingcloud--hystrix(断路器)