SpringCloud-04-Hystrix

本文详细介绍了Hystrix在SpringCloud中的应用,包括服务熔断、降级和限流的概念及其实现。通过实际案例展示了如何配置Hystrix以防止级联故障,确保系统稳定性。通过对断路器的工作原理、服务降级策略、限流措施的探讨,帮助读者理解如何利用Hystrix提升分布式系统的容错能力。
摘要由CSDN通过智能技术生成

服务雪崩:

如果扇出的链路上某个微服务的响应超时或者不可用,对微服务调用方的调用会占用越来越多的系统资源,进而引起系统崩溃,这就是"雪崩效应"。

对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和,比失败更糟糕的是,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不会导致整个应用程序或系统崩溃。

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

1. 熔断、降级、限流

1.1 熔断

先介绍一下断路器,断路器本身是一种开关装置,而服务熔断指的就是断路器打开。
当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的,可处理的备选响应(降级FallBack),而不是长时间的等待或者抛出调用方法处理的异常,当检测到该节点微服务调用响应正常后恢复调用链路。

断路器工作过程:服务的降级->进而熔断->恢复调用链路

  1. 请求达到统计时长(metrics.rollingStats.timeInMilliseconds 统计滚动窗口的持续时间)
  2. 假设电路上的请求量达到某个阈值(circuitBreaker.requestVolumeThreshold 最小请求数)
  3. 并假设误差百分比超过阈值误差百分比(circuitBreaker.errorThresholdPercentage 失败率达到多少后开启)
  4. 然后,断路器从转换CLOSED为OPEN。
  5. 当它断开时,它会使针对该断路器的所有请求短路。
  6. 经过一段时间(circuitBreaker.sleepWindowInMilliseconds 跳闸后拒绝请求的时间)后,下一个单个请求被允许通过(这是HALF-OPEN状态)。
    如果请求失败,继续进入步骤5,则断路器将OPEN在睡眠窗口持续时间内返回到该状态。
    如果请求成功,断路器将切换到,CLOSED并且步骤1中的逻辑将再次接管。

1.2 服务降级

服务降级:简单的说就是当当前服务有问题(异常或超时)时,向调用方返回一个符合预期的,可处理的备选响应。

哪些情况会触发降级:

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

1.3 限流

Hystrix中的限流,一般表现为线程池/信号量满负荷后,进入降级方法,进而达到限流的效果。

说明: 本文的所有代码,都是在前面文章的代码基础上开发的,也就是已经搭建好了Eureka注册中心和服务消费者已经配置好了Ribbon,Eureka注册中心和Ribbon客户端负载均衡这两个组件还不了解的朋友可以先去看看前面的文章《SpringCloud-01-Eureka》和《SpringCloud-02-Ribbon》。

2. 未使用Hystrix演示级联故障

2.1 模拟服务提供者业务处理异常,服务消费者级联故障

模块provider-payment9001也就是服务提供者paymentService,在PaymentController中添加一个模拟业务处理异常的接口

@RestController
@RequestMapping("/payment")
public class PaymentController {
   

    @Value("${server.port}")
    private String serverPort;
...
    /**
     * 模拟请求异常
     *
     * @return
     */
    @GetMapping(value = "/excep")
    public CommonResult<Payment> excep() {
   
        //模拟业务处理异常
        int div = 1 / 0;
        return CommonResult.newInstance().setCode(200).setMessage("查询成功,serverPort:" + serverPort).setData(null);
    }
...
}

模块consumer-order8001也就是服务消费者orderService,在OrderController中添加因为一个服务请求异常,导致级联故障的接口

@RestController
@RequestMapping("/order")
public class OrderController {
   
    public static final String PAYMENT_URL = "http://PAYMENTSERVICE";
...
    /**
     * 服务提供者请求异常,级联故障
     *
     * @return
     */
    @GetMapping(value = "/excep")
    public CommonResult<Payment> excep() {
   
        return this.restTemplate.getForObject(PAYMENT_URL + "/excep", CommonResult.class);
    }
...
}

分别启动服务提供者provider-payment9001和服务消费者consumer-order8001,在浏览器请求 http://localhost:8001/order/excep,发现出现级联故障

服务消费者orderService出现了500 状态码。

consumer-order8001发送网络请求后,因为provider-payment9001业务处理异常导致consumer-order8001的级联故障,打印的日志如下:

org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 : [{"timestamp":"2021-05-18T02:40:59.394+0000","status":500,"error":"Internal Server Error","message":"/ by zero","path":"/payment/excep"}]
	at org.springframework.web.client.HttpServerErrorException.create(HttpServerErrorException.java:100) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:172) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:112) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:785) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:743) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:318) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at com.xander.order8001.controller.OrderController.excep(OrderController.java:39) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_191]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_191]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_191]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_191]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]	...

2.2 模拟服务提供者业务处理响应超时,服务消费者级联故障

在模块provider-payment9001的PaymentController中添加一个模拟业务处理响应超时的接口

@RestController
@RequestMapping("/payment")
public class PaymentController {
   

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

    /**
     * 模拟请求超时
     *
     * @return
     * @throws InterruptedException
     */
    @GetMapping(value = "/timeout")
    public CommonResult<Payment> timeout() throws InterruptedException {
   
        //模拟业务处理时间,线程睡眠3s
        TimeUnit.SECONDS.sleep
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值