zoukankan      html  css  js  c++  java
  • springcloud(八)-Hystrix熔断器

    雪崩效应

    在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。

    如果下图所示:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。

    如何容错

    要想防止雪崩效应,必须有一个强大的容错机制。该机制需实现以下两点:

    • 为网络请求设置超时:必须为网络请求设置超时。正常情况下,一个远程调用一般在几十毫秒内就能得到响应。如果依赖的服务不可用或者网络有问题,那么响应时间就会变得很长(几十秒)。通常情况下,一次远程调用对应着一个线程或进程。如果响应太慢,这个线程或进程就得不到释放。而线程或进程又对应着系统资源,如果得不到释放的线程或进程越积越多,资源就会逐渐被耗尽,最终导致服务的不可用。因此,必须为每个网络请求设置超时,让资源尽快释放。
    • 使用断路模式:试想一下,如果家里没有断路器,当电流过载时(例如功率过大、断路等),电路不断开,电路就会升温,甚至可能烧断电路、引发火灾。使用断路器,电路一旦过载就会跳闸,从而可以保护电路的安全。在电路超载的问题被解决后,只需关闭断路器,电路就可以恢复正常。

    同理,如果对某个微服务的请求有大量超时(常常说明该微服务不可用),再去让新的请求访问该服务已经没有意义了,只会无畏的消耗资源。例如,设置了超时时间为1s,如果短时间内有大量的请求无法在1s内得到响应,就没有必要再去请求依赖的服务了。

    断路器可理解为对容易导致错误的操作的代理。这种代理能够统计一段时间内调用失败的次数,并决定是正常请求依赖的服务还是直接返回。

    断路器可以实现快速失败,如果它在一段时间内检测到许多类似的错误(例如超时),就会在之后的一段时间内,强迫对该服务的调用快速失败,即不再请求所依赖的服务。这样,应用程序就无需再浪费CPU时间去等待长时间的超时。

    断路器也可自动诊断依赖的服务是否已经恢复正常。如果发现依赖的服务已经恢复正常,那么就会恢复请求该服务。使用这种方式,就可以实现微服务的“自我修复”——当依赖的服务不正常时,打开断路器时快速失败,从而防止雪崩效应;当发现依赖的服务恢复正常时,又会恢复请求。

    断路器状态转换的逻辑图:

    简单说一下:

    正常情况下,断路器关闭,可以正常请求依赖的服务。

    当一段时间内,请求的失败率达到一定阈值(例如错误率达到50%,或100次/分钟等),断路器就会打开。此时,不会再去请求依赖的服务。

    断路器打开一段时间后,会自动进入“半开”状态。此时,断路器可允许一个请求访问依赖的服务。如果该请求能够调用成功,则关闭断路器;否则继续保持打开状态。

    综上,我们可通过以上两点机制保护应用,从而防止雪崩效应并提升应用的可用性。

    Hystrix简介

    Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

    • 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。
    • 跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
    • 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往改以来的请求就被立即拒绝,而不是排队等候,从而加速失败判定。
    • 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时和被拒绝的请求等。
    • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。
    • 自我修复:断路器打开一段时间后,会自动进入“半开”状态。断路器打开、关闭、半开的逻辑转换,见上图。

    代码编写

    1.我们复制microservice-consumer-movie-ribbon,将ArtifactId修改为microservice-consumer-movie-ribbon-hystrix。

    2.为项目添加依赖。

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

    3.在启动类上添加注解@EnableCircuitBreaker或@EnableHystrix,从而为项目开启断路器支持。

    4.修改MovieController,让其中的findById方法具备容错能力。

    @RestController
    public class MovieController {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private LoadBalancerClient loadBalancerClient;
    
        @HystrixCommand(fallbackMethod = "findByIdFallback")
        @GetMapping("/user/{id}")
        public User findById(@PathVariable Long id) {
            return restTemplate.getForObject("http://microservice-provider-user/" + id, User.class);
        }
    
        public User findByIdFallback(Long id) {
            User user = new User();
            user.setId(-1L);
            user.setName("默认用户");
            return user;
        }
    
        @GetMapping("log-user-instance")
        public void logUserInstance() {
            ServiceInstance serviceInstance = loadBalancerClient.choose("microservice-provider-user");
            LOGGER.info("{}:{}:{}", serviceInstance.getServiceId(), serviceInstance.getHost(), serviceInstance.getPort());
    
        }
    
    }

    由代码可知,为findById方法编写了一个回退方法findByIdFallback,该方法与findById方法具有相同的参数与返回类型,该方法返回了一个默认的User。

    在findById方法上,使用注解@HystrixCommand的fallbackMethod属性,指定回退方法是findByIdFallback。

    测试

    启动项目microservice-discovery-eureka。

    启动项目microservice-provider-user。

    启动项目,microservice-consumer-movie-ribbon-hystrix。

    访问http://localhost:8082/user/1,结果:

    {"id":1,"username":"account1","name":"张三","age":20,"balance":98.23}

    断开microservice-provider-user,再次访问 http://localhost:8082/user/1,结果:

    {"id":-1,"username":null,"name":"默认用户","age":null,"balance":null}

    说明当前用户微服务不可用,进入了回退方法。


    异常展示

    很多时候,我们需要获得造成回退的原因,只需再fallback方法上添加一个Throwable参数即可:

    public User findByIdFallback(Long id ,Throwable throwable) {
            LOGGER.error("进入回退方法,异常:", throwable);
            User user = new User();
            user.setId(-1L);
            user.setName("默认用户");
            return user;
        }

    断开microservice-provider-user,再次访问 http://localhost:8082/user/1,看电影微服务的控制台:

    2019-04-23 19:34:05.539 ERROR 10392 --- [ieController-10] c.i.cloud.controller.MovieController     : 进入回退方法,异常:
    
    java.lang.IllegalStateException: No instances available for microservice-provider-user
        at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:75) ~[spring-cloud-netflix-core-1.4.0.RELEASE.jar:1.4.0.RELEASE]
        at org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor.intercept(LoadBalancerInterceptor.java:55) ~[spring-cloud-commons-1.3.0.RELEASE.jar:1.3.0.RELEASE]
        at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:86) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.cloud.netflix.metrics.MetricsClientHttpRequestInterceptor.intercept(MetricsClientHttpRequestInterceptor.java:64) ~[spring-cloud-netflix-core-1.4.0.RELEASE.jar:1.4.0.RELEASE]
        at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:86) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:70) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:659) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:620) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:294) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.web.client.RestTemplate$$FastClassBySpringCGLIB$$aa4e9ed0.invoke(<generated>) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
        at org.springframework.cloud.netflix.metrics.RestTemplateUrlTemplateCapturingAspect.captureUrlTemplate(RestTemplateUrlTemplateCapturingAspect.java:33) ~[spring-cloud-netflix-core-1.4.0.RELEASE.jar:1.4.0.RELEASE]
        at sun.reflect.GeneratedMethodAccessor94.invoke(Unknown Source) ~[na:na]

    还有一种情况,当发生业务异常时,我们不想触发fallback。此时要怎么办呢?Hystrix有个HystrixBanRequestException类,这是一个特殊的异常类,当该异常发生时,不会触发回退。因此,可将自定义的业务异常继承该类,从而达到业务异常不回退的效果。

    另外,@HystrixCommand为我们提供了ignoreExceptions属性,也可借助该属性来配置不想执行回退的异常类。例如:

    @HystrixCommand(fallbackMethod = "findByIdFallback",ignoreExceptions= {IllegalArgumentException.class})
        @GetMapping("/user/{id}")
        public User findById(@PathVariable Long id) {
            return restTemplate.getForObject("http://microservice-provider-user/" + id, User.class);
        }

    多个异常类用逗号隔开。

    这样,即使在findById中发生IllegalArgumentException,也不会执行findByIdFallback方法。


    代码下载地址:https://gitee.com/fengyuduke/my_open_resources/blob/master/microservice-consumer-movie-ribbon-hystrix.zip

  • 相关阅读:
    asp.net mvc(2013424)——基本知识
    asp.net mvc(2013425)——使用模板页
    jquery实现tab切换核心代码
    asp.net mvc(2013422 )——准备入门
    也说C#串行化
    Net Assembly.GetExecutingAssembly() 和 Assembly.GetCallingAssembly()的区别
    log (一)
    C# 重载和从写的区别
    log4net
    C# 反射
  • 原文地址:https://www.cnblogs.com/fengyuduke/p/10758313.html
Copyright © 2011-2022 走看看