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}

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


未完待续,代码下次提供。。。。。

猜你喜欢

转载自www.cnblogs.com/fengyuduke/p/10758313.html