Spring Cloud Hystric简介与应用

1、前言

在微服务架构体系下,服务间不可避免地会发生依赖关系,并且一般都会通过REST AP来进行通信。在理想情况下,什么问题都不发生当然是最好的。但服务运行期间难免会出现各种问题,如网络阻塞,延迟过高,甚至服务直接挂掉等情况都是很有可能发生的。如果服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应或超时。在高负载的情况下,如果不做任何处理的话,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的奔溃。我们把这种因提供者的不可用从而导致消费者不可用,并将不可用逐渐放大的现象称为雪崩效应。要想防止雪崩效应,必须有一个强大的容错机制。该机制需实现以下两点:(1)为网路请求设置超时;(2)使用断路器模式。Hystrix就是一个实现超时机制与断路器模式的工具类库。

2、Hystrix简介

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

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

3、Hystrix的工作流程

下图来自Hystrix官网, 详细描述了Hystrix的工作流程

图片中序号解释如下:

1、构造一个 HystrixCommand或HystrixObservableCommand对象, 用于封装请求, 并在构造方法配置请求被执行需要的参数;

2、执行命令, Hystrix提供了4种执行命令的方法, execute和queue 适用于HystrixCommand对象, 而observe和toObservable适用于HystrixObservableCommand对象;

3、判断是否使用缓存响应请求, 若启用了缓存, 且缓存可用, 直接使用缓存响应请求. Hystrix支持请求缓存, 但需要用户自定义启动;

4、判断熔断器是否打开, 如果打开, 跳到第8步;

5、判断线程池/队列/信号量是否已满, 已满则跳到第8步;

6、执行HystrixObservableCommand.construct或HystrixCommand.run, 如果执行失败或者超时, 跳到第8步; 否则, 跳到第9步;

7、统计熔断器监控指标;

8、走Fallback备用逻辑;

9、返回请求响应.

4、Hystrix功能实现

Hystrix实际应用场景很多,比如隔离、限流、降级、熔断、监控等,以下只做了降级、熔断的功能实现。

首先构建一个项目环境,包含如下几个项目,测试均为spring boot单体项目

Eureka注册中心:demo-spring-eureka

提供者:demo-spring-provider

消费者:demo-spring-consumer

4.1、服务降级

4.1.1 从消费者端进行服务降级

demo-spring-consumer使用feign做服务间的调用,引入feign依赖,因为Feign中已经包含Hystrix功能,故不重复引入hystrix包

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

在介绍hystrix具体功能实现之前,我们先来了解下@FeignClient注解标签的常用属性:

  • name:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
  • url: url一般用于调试,可以手动指定@FeignClient调用的地址
  • decode404:当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException
  • configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
  • fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口
  • fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
  • path: 定义当前FeignClient的统一前缀

方式1:修改Feign接口,使用@ FeignClient注解的fallback属性为指定名称的Feign客户端添加回退

@FeignClient(name = "demo-spring-provider",fallback = TestApiFeignFallback.class)
public interface TestApiFeign {
    /**
     * 一个测试feign方法
     * @return
     */
    @RequestMapping(value = "/info",method = RequestMethod.GET)
    String testA();
}

@Component
public class TestApiFeignFallback implements TestApiFeign {
    @Override
    public String testA() {
        return "调用失败";
    }
}

方式2:修改Feign接口,使用@ FeignClient注解的fallbackFactory属性为指定名称的Feign客户端添加回退

@FeignClient(name = "demo-spring-provider",fallbackFactory = TestApiFeignFactory.class)
public interface TestApiFeign {
    /**
     * 一个测试feign方法
     * @return
     */
    @RequestMapping(value = "/info",method = RequestMethod.GET)
    String testA();
}

@Component
@Slf4j
public class TestApiFeignFactory implements FallbackFactory<TestApiFeign> {
    @Override
    public TestApiFeign create(Throwable cause) {
        return new TestApiFeign() {
            @Override
            public String testA() {
                log.info("falback cause was:", cause);
                return cause.getMessage();
            }
        };
    }
}

以上两种方式都能实现服务降级,fallback不能捕获异常打印堆栈信息,不利于问题排查,因此不推荐。fallbackFactory可以捕获异常信息并返回默认降级结果,可以打印堆栈信息,推荐使用。

4.1.2 从提供者端进行服务降级

首先在提供者端加入Hystrix的依赖

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

demo-spring-provider服务中改造feign接口方法实现,添加@HystrixCommand注解。

@RequestMapping(value = "/info", method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "TimeOutErrorHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
public String testA() {
    try {
        //方法休眠5秒,一定会发生错误,也就会调用下边的fallbakcMethod方法
        TimeUnit.SECONDS.sleep(4);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "服务正常调用";
}


/**
 * 这个就是当上边方法的“兜底”方法
 */
public String TimeOutErrorHandler() {
    return "对不起,系统处理超时";
}

@HystrixCommand注解要注意三点:

  • FallCallbackMethod中的这个参数就是“兜底”方法,错误回退时触发
  • fallCallbackMethod中的这个方法的声明要和实际方法名一致
  • commmandProperties属性中可以写多个@HystrixProperty注解,其中的name和value就是配置对应的属性,上例中的这个就是配置响应超时

最后,在启动类上加上@EnableCircuitBreaker注解

@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker
public class DemoSpringProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoSpringProviderApplication.class, args);
    }
}

以上示例理论上是要返回“服务调用正常”,但是呢,由于我们人为造成了超时错误,所以就一定会返回fallback中的“对不起,系统处理超时”,而且这个返回是会在2秒后。

4.2 服务熔断

当启用服务降级时,会默认启用服务熔断机制,我们只需要对一些参数进行配置就可以了,就是在上边的@HystrixCommand中的一些属性,比如:

@HystrixCommand(fallbackMethod = "TimeOutErrorHandler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "2000"),
@HystrixProperty(name="circuitBreaker.enabled",value="true"),//开启断路器   
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value="20"),//请求次数的峰值    
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="10000"),//检测错误次数的时间范围    
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="60")//请求失败率达到多少比例后会打开断路器})

更多配置可以参考 https://github.com/Netflix/Hystrix/wiki/Configuration

服务熔断,就好比我们家中的保险丝。当检测到家中的用电负荷过大时,就断开一些用电器,来保证其他的可用。在分布式系统中,就是调用一个系统时,在一定时间内,这个服务发生的错误次数达到一定的值时, 我们就打开这个断路器,不让调用过去,而是让他直接去调用降级方法。再过一段时间后,当一次调用时,发现这个服务通了,就将这个断路器改为“半开”状态,让调用一个一个的慢慢过去,如果一直没有发生错误,就将这个断路器关闭,让所有的服务全部通过。

值得注意的是:执行回退逻辑并不代表断路器已经打开,请求失败、超时、被拒绝以及断路器打开时都会执行回退逻辑。只有当请求的失败率达到阈值(默认是5秒内20次失败),断路器才会打开。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值