Spring Cloud之Hystrix入门


Spring Cloud之Hystrix入门

hystrix对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制,这与hystrix本身的功能不谋而合,因此Netflix团队将该框架命名为Hystrix,并使用了对应的卡通形象做作为logo。

img

概念

  1. 服务熔断
    ​ 一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施,所以很多地方把熔断亦称为过载保护。很多时候刚开始可能只是系统出现了局部的、小规模的故障,然而由于种种原因,故障影响的范围越来越大,最终导致了全局性的后果。
    适用场景:防止应用程序直接调用那些很可能会调用失败的远程服务或共享资源

  2. 服务降级:
    ​ 当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

  3. 熔断器Circuit-Breaker

    Circuit-Breaker模式可以防止应用重复的尝试调用容易失败的操作,当Circuit-Breaker模式判断错误会持续的时候,它会令操作不再持续等待,以免继续浪费CPU资源。当然,Circuit-Breaker模式也令应用本身可以发现错误有没有被修复。如果发生的问题已经被修复了,应用可以重新尝试去调用服务。

    Circuit-Breaker模式的目的和Retry模式的目的是不同的。Retry模式令应用不断的重试调用,直到最后成功。而Circuit-Breaker模式是阻止应用继续尝试无意义的请求。应用可以同时使用两种模式。然而,重试逻辑应用对于所有的Circuit-Breaker返回的异常十分敏感,这样可以在Circuit-Breaker发现错误短时间无法修复的情况下直接不再继续重试。

    Circuit-Breaker的作用就好似可能失败操作的代理。代理会监控最近发生的错误,然后依据这一信息来决定是否允许操作的继续执行,或者直接立刻返回异常信息。

    Circuit-Breaker可以按照如下的状态来模仿一个断路器来实现:

    • 关闭:应用的请求已经路由到了这个操作。代理应该维护最近一段时间的错误信息,如果调用操作失败,那么大力增加这个错误信息的数量。如果这个错误数量超过给定时间的阈值,代理进入到打开状态。这个时候,代理启动一个超时的Timer,当Timer过期了,代理则进入半开状态。

    超时Timer的目的是为了给系统一段时间来自我修复之前碰到的问题。

  • 打开:令可能失败的外部调用操作立刻失败,所有的外部调用直接抛异常给应用。
  • 半开:只有一定数量的应用请求可以进行操作的调用。如果这些请求成功了,那么就假定之前发成的错误已经被系统自动修复了,而Circuit-Breaker转换成关闭状态(同时重置错误计数器)。如果任何请求失败了,那么Circuit-Breaker会假定错误仍然在存在,Circuit-Breaker会重新转换成打开状态,并重启超时Timer给系统更多的时间来自我修复错误。

半开状态可以很有效的阻止一个可以恢复的服务被大量的请求所淹没。而处于恢复中的服务,可能也能够承载一定数量的请求,直到完全恢复才能恢复全部的吞吐量。但是,突然大量的错误也可能会令恢复中的服务重新crash掉。
参考如下状态变换图:

屏幕快照 2018-11-20 下午9.40.57

需要注意的是,上图中,关闭状态所用的错误计数器是基于时间的。它会以一定的时间间隔来重置。这也能够在常见错误的情况下不让Circuit-Breaker模式进入打开状态。而错误计数阈值才会令Circuit-Breaker进入到打开状态,只有当指定时间间隔内,错误计数达到阈值才能令Circuit-Breaker进入到打开状态。半开状态所使用的成功计数器则会记录成功的调用次数。Circuit-Breaker如果在之后出现了连续的成功的调用,那么Circuit-Breaker就会进入关闭状态。如果任何调用的失败了,那么Circuit-Breaker也会重新进入到打开状态,成功计数器也会重置,直到下次重新进入到半开状态。

通常系统的外部恢复,很多时候都是通过重启失败的组件或者修复网络连接来完成的。

实现Circuit-Breaker模式可以增加系统的稳定性和弹性,当系统从错误恢复的时候,可以尽可能所有失败对系统性能的影响。Circuit-Breaker模式可以通过拒绝外部调用来保证服务的响应时间,而不是等待操作的超时(或者持续阻塞)。如果Circuit-Breaker在每一次状态改变的时候触发一些事件的话,这个状态的改变也可以用来监视Circuit-Breaker保护模块的健康状态,或者是对监控Circuit-Breaker的管理员发出警告,Circuit-Breaker已经进入了打开状态。

Circuit-Breaker模式可以很好的定制并适配很多可能的错误。举例来说,开发者可以应用一个增长的超时Timer,也可以直接令Circuit-Breaker在处于打开状态几秒,如果错误在之后还没有解决,就超时几分钟等等。在有些场景下,打开状态的Circuit-Breaker也可以不抛出异常而是返回默认值来改善应用的响应。

Hystrix简介

Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。(防止雪崩)

Hystrix的设计原则包括:

  • 资源隔离
  • 熔断器
  • 命令模式

Hystrix通过将每个依赖服务分配独立的线程池进行资源隔离, 从而避免服务雪崩.

在一个分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,如何能够保证在一个依赖出问题的情况下,不会导致整体服务失败,这个就是Hystrix需要做的事情。Hystrix提供了熔断、隔离、Fallback、cache、监控等功能,能够在一个、或多个依赖同时出现问题时保证系统依然可用。

在复杂的分布式架构的应用程序有很多的依赖,都会不可避免地在某些时候失败。高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险。

Hystrix主要解决哪些问题

  • 对外依赖包括第三方类库的依赖提供延迟和失败保护
  • 阻断传递失败,防止雪崩(一个服务的延迟会导致单位时间内资源一直被占用,应用的其它请求进来也会延迟,紧接着队列开始堆积,线程还有其他系统资源不释放,甚至引发整个系统的级联失败。)
  • 快速失败并即时恢复
  • 合理的fallback和优雅降级
  • 提供近实时的监控、告警和操作控制

Hystrix的解决方案

  • hystrix把每个依赖都进行隔离,对依赖的调用全部包装成HystrixCommand或者HystrixObservableCommand
  • 对依赖的调用耗时设置阀值,如果超过阀值直接判定超时
  • 对每个依赖维护一个连接池,如果连接池满直接拒绝访问
  • hystrix评估调用失败,调用超时,线程拒绝,调用成功的比例,如果超过指定的阀值直接走熔断处理,对依赖的访问直接走fallback逻辑(fallback逻辑使用者自己实现)
  • 熔断生效后,会在设定的时间后放出一个请求来探测依赖是否恢复,依赖的应用恢复后关闭熔断
  • 修改hystrix配置近实时生效

使用示范

引入依赖

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

开启熔断 (添加注解)

在启动类原有的基础上添加@EnableCircuitBreaker注解即可

@SpringCloudApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }

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

@SpringCloudApplication = @SpringBootApplication + @EnableDiscoveryClient + @EnableCircuitBreaker

改造代码

添加注解HystrixCommand并且设置熔断处理fallbackMethod 逻辑。此函数返回类型,参数列表必须与原函数一致!

@HystrixCommand(fallbackMethod = “queryUserByIdFallback”)


@HystrixCommand(fallbackMethod = "queryUserByIdFallback")
   public User queryForObjects(@PathVariable("id")Integer id) {
       //加入负载均衡 只需将主机名部分换成服务即可,通过拦截器实现
       String url  = "http://user-service/user/"+id;
       System.out.println(url);
       User user = restTemplate.getForObject(url, User.class);
       return user;
   }
   public User queryUserByIdFallback(Integer id){
       User user = new User();
       user.setId(id);
       user.setName("用户信息查询出现异常!");
       return user;
   }

也可以为某个类添加默认熔断处理

@DefaultProperties(defaultFallback = “queryUserByIdFallback”)

@RestController
@RequestMapping("consumer")
@DefaultProperties(defaultFallback = "queryUserByIdFallback")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("{id}")
    @HystrixCommand
    public User queryForObjects(@PathVariable("id") Integer id) {
        //加入负载均衡 只需将主机名部分换成服务即可,通过拦截器实现
        String url = "http://user-service/user/" + id;
        System.out.println(url);
        User user = restTemplate.getForObject(url, User.class);
        return user;
    }
	//这里为了掩饰方便才这样写的 实际上应该封装一个结果类返回的。
    public User queryUserByIdFallback(Integer id) {
        User user = new User();
        user.setId(id);
        user.setName("用户信息查询出现异常!");
        return user;
    }
}

为HystrixCommand设置超时时长属性

由于没有文档,只能前往源码中寻找

![屏幕快照 2018-11-20 下午9.10.30](Spring-Cloud之Ribbon负载均衡入门/屏幕快照 2018-11-20 下午9.10.30.png)

修改超时阀值

方法1

当执行超时(默认是1000毫秒),就会执行fallback函数

@HystrixCommand(commandProperties = {
      @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1200")
})

方法2

修改全局配置文件

有关熔断的属性

circuitBreaker.sleepWindowInMilliseconds //休眠时常
circuitBreaker.errorThresholdPercentage  //触发熔断的失败的百分比
circuitBreaker.requestVolumeThreshold //触发熔断的最小请求次数

文章作者: Bxan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Bxan !
  目录