Spring Cloud Hystrix 服务容错保护(Hystrix服务降级,依赖隔离,断路器)

文章知识 均来自以下系列文章,请参考原文

Spring Cloud 从入门到精通

Spring Cloud构建微服务架构:服务容错保护(Hystrix服务降级)【Dalston版】

Spring Cloud构建微服务架构:服务容错保护(Hystrix依赖隔离)【Dalston版】

Spring Cloud构建微服务架构:服务容错保护(Hystrix断路器)【Dalston版】 

 springcloud(四):熔断器Hystrix

服务容错保护&服务容错保护框架

服务容错保护:在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元应用间通过服务注册与订阅的方式互相依赖。由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行。

这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会出现因 等待  出现故障的依赖方响应  而形成任务积压,线程资源无法释放,最终导致自身服务的瘫痪,甚至出现故障的蔓延 最终导致整个系统的瘫痪。
如果这样的架构存在如此严重的隐患,那么相较传统架构就更加的不稳定。为了解决这样的问题,因此产生了断路器等一系列的服务保护机制。

Netflix的开源框架 Hystrix:目标在于通过控制 那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备了服务降级、服务熔断、线程隔离、请求缓存、请求合并以及服务监控等强大功能。依赖隔离、服务降级和断路器,这三个重要利器构成了Hystrix实现服务容错保护的强力组合拳。

Spring Cloud Hystrix:基于Netflix的开源框架 Hystrix实现,实现了服务降级,线程隔离、断路器等一系列的服务保护功能。

 

1.Spring Cloud Hystrix 实现服务降级

对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.

step1:创建Spring Boot项目eureka-consumer-ribbon-hystrix,添加起步依赖spring-cloud-starter-netflix-eureka-server,spring-cloud-starter-netflix-ribbon,spring-cloud-starter-netflix-hystrix

step2:在项目入口添加 @EnableDiscoveryClient 注解,将当前应用加入到服务治理体系中。在项目入口添加 @EnableHystrix或者@EnableCircuitBreaker注解,开启Hystrix的使用

或者在项目入口 使用@SpringCloudApplication注解。它可以代替 @SpringBootApplication @EnableDiscoveryClient,@EnableCircuitBreaker这3个注解。这也说明Spring Cloud项目肯定是Spring Boot项目,而且Spring Cloud标准应用 应该包含服务发现以及断路器

Step3:在application.properties文件添加配置

#微服务的名称:后续在调用的时候只需要使用该名称就可以进行服务的访问
spring.application.name=eureka-consumer-ribbon-hystrix
#微服务端口
server.port=2401
 
#指定服务注册中心的位置
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/
View Code

Step4:在配置类 配置ribbon  。通过ribbon 实现服务调用

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

Step5:定义 服务调用service

使用@HystrixCommand来为一个依赖资源定义服务降级逻辑。实现方式非常简单,同时对于降级逻辑还能实现一些更加复杂的级联降级等策略。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class EurekaClientService {

    @Autowired
    private RestTemplate restTemplate;

    // 在消费者服务方法中,增加@HystrixCommand注解来指定服务降级方法
    @HystrixCommand(fallbackMethod = "fallback")
    public String consumer() {
        String services = restTemplate.getForObject("http://eureka-client/discoveryClient", String.class);
        return services;
    }

    // 定义服务降级方法,如果调用eureka-client/discoveryClient,报错或延迟,就会执行到这个方法。
    public String fallback() {
        System.out.println("aho , call eureka-client failed. this is fallback!");
        return "aho , call eureka-client failed. this is fallback!";
    }
}
View Code

Step6:定义controller,调用  微服务调用service

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DiscoveryClientController {

    @Autowired
    private EurekaClientService eurekaClientService;

    @GetMapping("/consumer")
    public String consumer() {
        String services = eurekaClientService.consumer();
        return services;
    }
}
View Code

Step7:启动eureka-server,eureka-client,eureka-consumer-ribbon-hystrix。并访问http://localhost:2401/consumer 。就会看到eureka-consumer-ribbon-hystrix 服务是如何正常消费eureka-client服务的/discoveryClient接口的

Step8:触发服务降级逻辑,将服务提供者eureka-client的逻辑加一些延迟,比如:睡眠5秒


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DiscoveryClientController {

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/discoveryClient")
    public String discoveryClient() throws InterruptedException {
        // 服务延迟 睡眠5秒
        Thread.sleep(5000L);

        // 服务实例相关内容
        String service = "Services: " + discoveryClient.getServices();

        // 打印出服务实例相关内容
        System.out.println(service);

        // 返回服务实例相关内容
        return service;
    }
}
View Code

Step9:重启eureka-client服务,再次访问http://localhost:2401/consumer。就会看到返回 aho this is fallback

Step10:总结:我们从eureka-client的控制台中,可以看到服务提供方输出了原本要返回的结果,但是由于返回前延迟了5秒,而服务消费方触发了服务请求超时异常,服务消费者就通过HystrixCommand注解中指定的降级逻辑进行执行,因此该请求的结果返回了fallback。这样的机制,对自身服务起到了基础的保护,同时还为异常情况提供了自动的服务降级切换机制。

 

2.依赖隔离,资源隔离:

依赖隔离、服务降级在使用时候都是一体化实现的,这样利用Hystrix来实现服务容错保护在编程模型上就非常方便的,并且考虑更为全面。所以,我们在定义服务降级的时候,已经自动的实现了依赖隔离。 在我们使用了@HystrixCommand来将某个函数包装成了Hystrix命令,这里除了定义服务降级之外,Hystrix框架就会自动的为这个函数实现调用的隔离。

 

3.断路器

断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.

什么是断路器:

Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.

有了服务降级,为什么还需要断路器

根据上文,服务降级可以避免服务提供者的不可用 带来故障的蔓延 最终导致整个系统的瘫痪。那为什么还需要断路器呢?因为服务降级虽然可以避免故障蔓延,但是会导致请求堆积,也可能会造成系统的瘫痪。比如上述例子,当我们把服务提供者eureka-client中加入了模拟的时间延迟之后,每个请求都会在当hystrix超时之后返回fallback,每个请求时间延迟就是近似hystrix的超时时间,如果设置为5秒,那么每个请求就都要延迟5秒才会返回,会造成不必要的时间堆积。但是当熔断器打开之后,再有请求调用的时候,将不会调用主逻辑,而是直接调用降级逻辑,这个时候就不会等待5秒之后才返回fallback。通过断路器,实现了自动地发现错误并把将降级逻辑切换为主逻辑,减少响应延迟的效果。

在服务消费端的服务降级逻辑因为hystrix命令调用依赖服务超时,触发了降级逻辑,但是即使这样,受限于Hystrix超时时间的问题,我们的调用依然很有可能产生堆积。比如短时间内有多个服务需要调用eureka-client,还是会造成请求堆积。 这个时候断路器就会发挥作用,直接切断原来的主逻辑调用,直接调用降级逻辑,就不用等待。

 

断路器打开的条件,断路器切断主逻辑的根据

断路器是在什么情况下开始起作用呢?这里涉及到断路器的三个重要参数:快照时间窗、请求总数下限、错误百分比下限。这个参数的作用分别是:

  • 快照时间窗:断路器确定是否打开需要统计的时间范围就是快照时间窗,默认为最近的10秒。
  • 请求总数下限:在快照时间窗内,必须满足请求总数下限才有资格根据熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用此时不足20次,即时所有的请求都超时或其他原因失败,断路器都不会打开。
  • 错误百分比下限:当请求总数在快照时间窗内超过了下限,比如发生了30次调用,如果在这30次调用中,有16次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%下限情况下,这时候就会将断路器打开。

总结:当Hystrix Command请求后端服务总数 在快照时间窗内  超过了下限  并且败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open)。这时所有请求会直接失败而不会发送到后端服务.

断路器的自动恢复功能

断路器打开之后,我们的降级逻辑已经被成了主逻辑,那么原来的主逻辑要如何恢复呢?当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑,当休眠时间窗到期,断路器将进入半开状态,释放下一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。

 

posted on 2020-07-03 18:45  dreamstar  阅读(168)  评论(0编辑  收藏  举报