返回 导航

SpringBoot / Cloud

hangge.com

SpringCloud - 服务容错保护组件Hystrix的使用详解1(基本用法)

作者:hangge | 2020-07-10 08:10

一、基本用法

1,Hystrix 介绍

  • Spring Cloud Hystrix 实现了断路器、线程隔离等一系列服务保护功能。
  • Spring Cloud Hystrix 也是基于 Netflix 的开源框架 Hystrix 实现的,该框架的目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
  • Hystrix 具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。

2,准备工作

(1)在使用 Spring Cloud Hystrix 实现断路器之前,准备并启动如下几个工程:
这些工程的实现可以参考我之前写的文章: SpringCloud - 服务注册与发现组件Eureka的使用详解4(服务发现与消费、负载均衡)

(2)默认情况下,当通过向 http://localhost:9000/hello-consumer 发起多次请求时,ribbon-consumerHELLO-SERVICE 的调用是负载均衡的(即轮流访问 80818082 这两个端口的服务)。

(3)如果此时关闭掉一个 HELLO-SERVICE 实例(比如 8082 端口的),在未加入断路器之前,如果访问轮询到这个实例上,会获得如下输出:

3,引入 Spring Cloud Hystrix

(1)首先编辑消费者 ribbon-consume 项目的 pom.xml 文件,添加 hystrix 依赖:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

(2)接着在项目的主类上添加 @EnableCircuitBreaker 注解开启断路器功能:
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class RibbonConsumerApplication {

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

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

(3)我们也可以直接使用 @SpringCloudApplication 注解来修饰主类,因为该注解就已经包含了 @SpringBootApplication@EnableDiscoveryClient@EnableCircuitBreaker 这三个注解。
@SpringCloudApplication
public class RibbonConsumerApplication {

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

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

(4)改造服务消费方式,新增 HelloService 类,在里面注入 RestTemplate 实例来调用 HELLO-SERVICE 服务,通过 @HystrixCommand 注解的 fallbackMethod 属性指定调用失败时的回调,即服务降级的实现方法:
    关于服务降级更详细的介绍与用法,可以查看我写的另一篇文章:SpringCloud - 服务容错保护组件Hystrix的使用详解3(定义服务降级)
@Service
public class HelloService {

    @Autowired
    RestTemplate restTemplate;

    // 通过 @HystrixCommand 注解的 fallbackMethod 属性指定服务降级的实现方法
    @HystrixCommand(fallbackMethod = "helloFallback")
    public String hello() {
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();
    }

    // 失败时的回调
    public String helloFallback() {
        return "error";
    }
}

(5)修改 Controller 类,注入上面实现的 Service 实例并进行调用:
@RestController
public class ConsumerController {
    @Autowired
    HelloService helloService;

    @GetMapping("/hello-consumer")
    public String helloConsumer() {
        return helloService.hello();
    }
}

(6)如果没有单独提取出一个 Service 实例,我们也可以直接将 @HystrixCommand 注解添加在 Controller 里面的方法上,效果是一样的:
@RestController
public class ConsumerController {
    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "helloFallback")
    @GetMapping("/hello-consumer")
    public String helloConsumer() {
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class)
                .getBody();
    }

    // 失败时的回调
    public String helloFallback() {
        return "error";
    }
}

(7)同样通过向 http://localhost:9000/hello-consumer 发起多次请求,ribbon-consumer 对两个 HELLO-SERVICE 实例会轮询调用。如果此时关闭掉一个 HELLO-SERVICE 实例(比如 8082 端口的),如果轮询到这个服务端上,输出的内容便为 error,而不再是之前的错误内容,说明 Hystrix 的服务回调生效。

附:服务超时触发熔断

    除了像上面一样通过断开具体的服务实例来模拟某个节点无法访问的情况之外,我们还可以模拟下服务阻塞(长时间未响应)的情况。

1,使用样例

(1)首先对服务提供者 HELLO-SERVICE /hello 接口做一些修改,通过 Thread.sleep() 函数使该接口的处理线程不是马上返回内容,而是在阻塞几秒之后才返回内容:
注意:由于 Hystrix 默认超时时间为 1000 毫秒,所以这里采用了 03000 的随机数以让处理过程有一定概率发生超时来触发断路器。
@RestController
public class HelloController {

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/hello")
    public String hello() throws Exception {
        //让处理线程随机等待几秒钟
        int sleepTime = new Random().nextInt(3000);
        Thread.sleep(sleepTime);
        return serverPort;
    }
}

(2)为了更精确地观察断路器的触发,在消费者调用函数中做一些时间记录:
@Service
public class HelloService {

    @Autowired
    RestTemplate restTemplate;

    // 通过 @HystrixCommand 注解来指定调用失败时的回调
    @HystrixCommand(fallbackMethod = "helloFallback")
    public String hello() {
        // 记录开始时间
        long start = System.currentTimeMillis();
        // 发起请求
        String result = restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class)
                .getBody();
        // 记录结束时间
        long end = System.currentTimeMillis();
        return result + " (耗时:" + (end - start) + "毫秒)";
    }

    // 失败时的回调
    public String helloFallback() {
        return "error";
    }
}

(3)重启服务提供者以及消费者实例,连续访问 http://localhost:9000/hello-consumer 几次,可以看到,如果请求响应在 2000 毫秒内则正常显示结果:

(4)否则就会返回 error,即服务消费者因调用的服务超时从而触发熔断请求,并调用回调逻辑返回结果。

2,设置断路器的超时时长

默认情况下,断路器的超时时长为 1000ms,我们可以在 application.properties 文件中通过配置属性进行修改:
#将断路器的超时时长设为10秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
评论

全部评论(0)

回到顶部