一、Hystrix介绍  

  Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

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

  github地址:https://github.com/Netflix/Hystrix

  服务降级  

  所谓降级,就是当某个服务出现异常之后,服务器将不再被调用,此时服务端可以自己准备一个本地的fallback回调,返回一个缺省值。 这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。

  可能出现服务降级的情况:

  • 程序运行异常
  • 服务超时
  • 服务熔断出发服务降级
  • 线程池/信号量打满也会导致服务降级

二、在提供者单独使用Hystrix

  • zookeeper-provider-payment8003

    pom.xml

        <!--hystrix熔断器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

    PaymentService

 1 package com.sdkj.service;
 2 
 3 import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
 4 import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
 5 import org.springframework.stereotype.Service;
 6 import org.springframework.web.bind.annotation.PathVariable;
 7 
 8 import java.util.UUID;
 9 import java.util.concurrent.TimeUnit;
10 
11 /**
12  * @Author wangshuo
13  * @Date 2022/5/23, 22:02
14  * Please add a comment
15  */
16 @Service
17 public class PaymentService {
18 
19     public String paymentInfo_OK(Integer id) {
20         return "线程池:" + Thread.currentThread().getName()
21                 + ",paymentInfo_OK,ID == " + id;
22     }
23 
24 
25     // fallbackMethod: 设置HystrixCommand服务降级所使用的方法名称,注意该方法需要与原方法定义在同一个类中,并且方法签名也要一致
26     // commandProperties: 设置HystrixCommand属性,如:断路器失败百分比、断路器时间容器大小等
27     // 设置断路器超时降级策略,时间3000毫秒超时
28     @HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler", commandProperties = {
29             @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
30     })
31     public String paymentInfo_Timeout(Integer id) {
32         int second = 5000;
33         try {
34             // 休眠5000毫秒
35             TimeUnit.MILLISECONDS.sleep(second);
36         } catch (InterruptedException e) {
37             e.printStackTrace();
38         }
39         // 异常
40 //        int n = 10/0;//测试异常降级
41         return "线程池:" + Thread.currentThread().getName()
42                 + ",paymentInfo_Timeout,ID == " + id
43                 + ",耗时" + second + "毫秒";
44     }
45 
46     public String paymentInfo_TimeoutHandler(Integer id) {
47         String result = "线程池:" + Thread.currentThread().getName()
48                 + ",paymentInfo_TimeoutHandler,ID == " + id;
49         return result;
50     }
51 
52     // 服务熔断
53     @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",
54             // 属性设置参考:HystrixCommandProperties
55             commandProperties = {
56                     // 是否开启断路器,默认true
57                     @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
58                     // 请求次数,默认20
59                     @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
60                     // 时间窗口期,默认5000
61                     @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
62                     // 失败率到达多少后跳闸,默认50
63                     @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")
64             })
65     public String paymentCircuitBreaker(@PathVariable("id") Integer id){
66         if(id < 0) {
67             throw  new RuntimeException("======== id 不能为负数 =========");
68         }
69         String serialNumber = UUID.randomUUID().toString();
70         return Thread.currentThread().getName() + "\t" + "调用成功,流水号:" + serialNumber;
71     }
72 
73     public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
74         return " id 不能为负数,请稍后再试, id == " + id;
75     }
76 
77 }

 

    PaymentController

 1 package com.sdkj.controller;
 2 
 3 import com.sdkj.service.PaymentService;
 4 import org.springframework.beans.factory.annotation.Autowired;
 5 import org.springframework.beans.factory.annotation.Value;
 6 import org.springframework.stereotype.Controller;
 7 import org.springframework.web.bind.annotation.RequestMapping;
 8 import org.springframework.web.bind.annotation.ResponseBody;
 9 
10 import java.util.UUID;
11 
12 /**
13  * @Author wangshuo
14  * @Date 2022/5/21, 19:14
15  * Please add a comment
16  */
17 @Controller
18 @RequestMapping(value = "/payment")
19 public class PaymentController {
20 
21     @Value("${server.port}")
22     private String serverPort;
23     @Autowired
24     PaymentService paymentService;
25 
26     @RequestMapping(value = "/getZk")
27     @ResponseBody
28     public String getPaymentZk(){
29 
30         return "spring cloud with zookeeper:" + serverPort + "\t" + UUID.randomUUID();
31     }
32 
33     //测试线程不降级
34     @RequestMapping(value = "/hystrix/ok")
35     @ResponseBody
36     public String getHystrixOk(Integer id){
37 
38         return paymentService.paymentInfo_OK(id);
39     }
40 
41     //测试超时降级
42     @RequestMapping(value = "/hystrix/timeout")
43     @ResponseBody
44     public String getHystrixTimeout(Integer id){
45 
46         return paymentService.paymentInfo_Timeout(id);
47     }
48 
49     //测试超时降级
50     @RequestMapping(value = "/hystrix/runtime")
51     @ResponseBody
52     public String getHystrixRuntime(Integer id){
53 
54         return paymentService.paymentCircuitBreaker(id);
55     }
56 }

 

    App

 1 package com.sdkj;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
 6 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 7 
 8 /**
 9  * @Author wangshuo
10  * @Date 2022/5/21, 19:11
11  * Please add a comment
12  */
13 //服务发现
14 @EnableDiscoveryClient
15 //启用熔断器
16 @EnableCircuitBreaker
17 @SpringBootApplication
18 public class App {
19 
20     public static void main(String[] args) {
21         SpringApplication.run(App.class, args);
22     }
23 }

三、消费者调用生产者使用Hystrix

  • zookeeper-consumer-order8004

    pom.xml

        <!--hystrix熔断器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

    OrderController

 1 package com.sdkj.controller;
 2 
 3 import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
 4 import org.springframework.beans.factory.annotation.Autowired;
 5 import org.springframework.stereotype.Controller;
 6 import org.springframework.web.bind.annotation.GetMapping;
 7 import org.springframework.web.bind.annotation.RequestMapping;
 8 import org.springframework.web.bind.annotation.ResponseBody;
 9 import org.springframework.web.client.RestTemplate;
10 
11 /**
12  * @Author wangshuo
13  * @Date 2022/5/21, 19:30
14  * Please add a comment
15  */
16 @Controller
17 @RequestMapping(value = "/order")
18 public class OrderController {
19 
20     private static final String url = "http://cloud-payment-service";
21     @Autowired
22     private RestTemplate restTemplate;
23 
24     @RequestMapping(value = "/getPaymentZk")
25     @ResponseBody
26     public String getPaymentZk() {
27 
28         return restTemplate.getForObject(url + "/payment/getZk", String.class);
29     }
30 
31     @GetMapping(value = "/hystrix/timeout")
32     // @HystrixCommand 支持服务降级
33     @HystrixCommand(fallbackMethod = "paymentDemoteFallback")
34     @ResponseBody
35     public String paymentTimeout(Integer id) {
36         //int n = 10/0;
37         return restTemplate.getForObject(url + "/payment/hystrix/timeout?id="+id, String.class);
38     }
39 
40     //降级回调方法
41     public String paymentDemoteFallback(Integer id){
42 
43         return "消费者:对方支付系统繁忙,请稍后再试,ID == "+id;
44     }
45 }

    App

 1 package com.sdkj;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.netflix.hystrix.EnableHystrix;
 6 
 7 /**
 8  * @Author wangshuo
 9  * @Date 2022/5/21, 19:30
10  * Please add a comment
11  */
12 //开启熔断器(这个注解包含了@EnableCircuitBreaker)
13 @EnableHystrix
14 @SpringBootApplication
15 public class App {
16 
17     public static void main(String[] args) {
18         SpringApplication.run(App.class, args);
19     }
20 }

四、Hystrix配合Feign进行通配服务降级

  1、在以上项目的基础上,新增接口实现类PaymentFallbackService,实现接口PaymentHystrixService

复制代码
 1 @Component
 2 public class PaymentFallbackService implements PaymentHystrixService {
 3     public String paymentInfo_OK(Integer id) {
 4         return "PaymentFallbackService——》paymentInfo_OK——》统一处理:" + id;
 5     }
 6 
 7     public String paymentInfo_Timeout(Integer id) {
 8         return "PaymentFallbackService——》paymentInfo_Timeout——》统一处理:" + id;
 9     }
10 }
复制代码

  2、修改接口PaymentHystrixService的配置,如下:

@Component
// openfeign客户端
// fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,
// fallback指定的类必须实现@FeignClient标记的接口
@FeignClient(value = "CLOUD-PAYMENT-SERVICE", fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {