Hystrix介绍

Hystrix是国外知名的视频网站Netflix所开源的非常流行的高可用架构框架。Hystrix能够完美的解决分布式系统架构中打造高可用服务面临的一系列技术难题,如雪崩。

Hystrix是处理依赖隔离的框架,将出现故障的服务通过熔断、降级等手段隔离开来,这样不影响整个系统的主业务(比如你得了传染病是不是要把你关起来隔离呢),同时也是可以帮我们做服务的治理和监控。

Hystrix的英文是豪猪,中文翻译为 熔断器,其思想来源于我们家里的保险开关,当家里出现短路,保险开关及时切掉电路,保证家里人员的安全,其目的就是起保护作用。

其设计原则如下:

1.防止单个服务异常导致整个微服务故障。
2.快速失败,如果服务出现故障,服务的请求快速失败,线程不会等待。
3.服务降级,请求故障可以返回设定好的二手方案数据(兜底数据)。
4.熔断机制,防止故障的扩散,导致整个服务瘫痪。
5.服务监控,提供了Hystrix Bashboard仪表盘,实时监控熔断器状态

Hystrix的功能

资源隔离
资源隔离包括线程池隔离和信号量隔离,作用是限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用 ,这里可以简单的理解为资源隔离就是限制请求的数量。

就好比在肺炎疫情爆发期间,是不是要限制人口的流动量,流动量越大可能会导致更多的肺炎患者出现。

线程池隔离
使用一个线程池来存储当前请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求先入线程池队列。这种方式要为每个依赖服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)

信号量隔离:

使用一个原子计数器(或信号量)记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)

java 接口熔断时间计算公式_spring boot


服务熔断

熔断机制是对服务链路的保护机制,如果链路上的某个服务不可访问,调用超时,发生异常等,服务会触发降级返回托底数据,然后熔断服务的调用(失败率达到某个阀值服务标记为短路状态),当检查到该节点能正常使用时服务会快速恢复。

简单理解就是当服务不可访问了或者服务器报错了或者服务调用超过一定时间没返回结果,就立马触发熔断机制配合降级返回预先准备的兜底数据返回,不至于长时间的等待服务的相应造成大量的请求阻塞,也不至于返回一些错误信息给客户端,而是返回一些兜底数据。

降级机制
超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。

简单理解就是服务降级就是当服务因为网络故障,服务器故障,读取超时等原因造成服务不可达的情况下返回一些预先准备好的数据给客户端。

在生活中服务降级到处可见,如在系统故障我们会返回友好的提示“服务暂时不可用”,或者如淘宝双11期间退款服务,留言服务暂时不可用,这个就是为了保证正常的购物流程相关服务没问题,然后人为的关闭一些不重要的服务,配合降级返回一些托底数据返回给用户(比如返回友好的提示信息“暂时不能退款”)。

缓存

提供了请求缓存、请求合并实现 , 在高并发的场景之下,Hystrix请求缓存可以方便地开启和使用请求缓存来优化系统,达到减轻高并发时请求线程的消耗、降低请求响应时间的效果。

Hystrix工作机制

正常情况下,断路器处于关闭状态(Closed),如果调用持续出错或者超时达到设定阈值,电路被打开进入熔断状态(Open),这时请求这个服务会触发快速失败(立马返回兜底数据不要让线程死等),后续一段时间内的所有调用都会被拒绝(Fail Fast),一段时间以后(withCircuitBreakerSleepWindowInMilliseconds=5s),保护器会尝试进入半熔断状态(Half-Open),允许少量请求进来尝试,如果调用仍然失败,则回到熔断状态,如果调用成功,则回到电路闭合状态;

java 接口熔断时间计算公式_java 接口熔断时间计算公式_02


那么我们这里先来实现hystrix和ribbon的配合使用

在order模块导入依赖:

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

在主配置类加上注解@EnableCircuitBreake

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker //开启熔断支持
public class OrderServerApplication {
省略部分代码...

接下来在controller类的访问方法上加上@HystrixCommand注解:,
指定降级方法,该方法就是浏览器返回给用户的提示信息

@RestController
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
   /* 通过 @HystrixCommand 标签标记方法熔断,标签的fallbackMethod属性指定拖地方法。
    那么当该方法在像远程服务发起调用出现异常,或是方法本身出现异常都会触发托底方法的执行,
    最终结果是托底方法的返回结果*/
    @HystrixCommand(fallbackMethod = "fallBackMethod")
    public User getUserById(@PathVariable("id") Long id) {
        String url = "http://user-server/user/" + id;
        return restTemplate.getForObject(url, User.class);
    }

    //降级方法 , 参数和返回值必须和被熔断的方法一致 ,方法名要和  fallbackMethod 的值一致
    public User fallBackMethod(@PathVariable("id") Long id){
        return new User(-1L,"找不到用户","用户服务异常","");
    }
}

重启order服务和关闭user服务,访问peer1:1030/order/1

java 接口熔断时间计算公式_java 接口熔断时间计算公式_03


说明熔断服务已经生效

上面我们是在方法上面加了一个@HystrixCommand注解,这样只能指定单独的方法支持熔断,我们可以在controller类上面加 @DefaultPropertie注解做统一配置,同时指定一个降级方法,那么所有的服务发起调用出现异常就会走公共的降级方法。但是需要注意公共的降级方法不能有参数,返回类型必须是User的类型或者它的子类类型,配置如下:

@RestController
@DefaultProperties(defaultFallback="fallBackMethod")
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;

 /* 通过 @HystrixCommand 标签标记方法熔断,标签的fallbackMethod属性指定拖地方法。
    那么当该方法在像远程服务发起调用出现异常,或是方法本身出现异常都会触发托底方法的执行,
    最终结果是托底方法的返回结果*/
    @RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
    @HystrixCommand
    public User getUserById(@PathVariable("id") Long id) {
        String url = "http://user-server/user/" + id;
        return restTemplate.getForObject(url, User.class);
    }


	   //公共降级方法
    public User fallBackMethod(){
        return new User(-1L,"找不到用户","用户服务异常","");
    }


}

在生产环境中我们可以让所有的方法都有相同的返回结果,如统一的JSON返回结果(JSONResult) ,那么在默认的降级方法中的返回类型就可以使用JSONResult了。

Hystrix参数配置

原文链接:

熔断参数配置
Hystrix提供了如下的几个关键参数
(com.netflix.hystrix.HystrixCommandProperties),来对一个熔断器进行配置:

  • hystrix.command.default.circuitBreaker.requestVolumeThreshold 滑动窗口的大小,默认为20
  • hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds 过多长时间熔断器再次检测是否开启默认5000(5s)
  • hystrix.command.default.circuitBreaker.errorThresholdPercentage 错误率,默认50%

3个参数放在一起,所表达的意思就是:每当20个请求中,有50%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。

hystrix: 
  command:
    default:
      circuitBreaker:
        requestVolumeThreshold: 20 #20个请求中
        errorThresholdPercentage: 50 #出错百分比阈值
        sleepWindowInMilliseconds: 50000 #短路5秒钟,尝试恢复

.超时设置
我们知道在服务的远程调用过程中,如果调用时间过久会触发Ribbon的超时,Hystrix也有超时配置hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
我们往往会把Ribbon和Hystrix的超时配合起来配置:

ribbon:
  MaxAutoRetries: 1 #最大重试次数
  MaxAutoRetriesNextServer: 1 #切换实例的重试次数
  OkToRetryOnAllOperations: false # 对所有的操作请求都进行重试,
  ConnectTimeout: 1000 #请求连接的超时时间
  ReadTimeout: 1800 #请求处理的超时时间
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000 #hystrix超时,置thread和semaphore两种隔离策略的超时时间

需要注意的是,我们设置了重试机制就需要Hystrix超时时间大于Ribbon超时时间,因为Hystrix超时后会直接熔断就不会再出发重试
hystrix超时=(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout

如果要针对于某个API做单独的超时设置也可以通过如下方式单独指定

@HystrixCommand(commandProperties = {
	@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
@RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
public User getById(@PathVariable("id")Long id){
省略部分代码

资源隔离模式
在Hystrix可以通过execution.isolation.strategy配置切换资源隔离模式:线程池或者信号量

hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: THREAD #默认是线程池,可以修改为信号量: SEMAPHORE

除了上面方式也可以使用如下方式单独指定:

@HystrixCommand(fallbackMethod = "", commandProperties=@HystrixProperty(name="execution.isolation.strategy", value="THREAD") )
 @RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
public User getById(@PathVariable("id")Long id){

最大请求设置

通过 execution.isolation.semaphore.maxConcurrentRequests 配置信号量模式下的最大请求数量(并发)

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 30000   #hystrix超时
          strategy: SEMAPHORE #默认是线程池,可以修改为信号量: SEMAPHORE
          semaphore:
            maxConcurrentRequests: 10 # SEMAPHORE模式下 , 1秒钟最大请求数量
            默认10

在线程池模式下通过 hystrix.threadpool.default.coreSize设置线程并发数量,
通过hystrix.threadpool.default.maxQueueSize 设置线程池队列模式,通过hystrix.threadpool.default.queueSizeRejectionThreshold设置排队数量 ,见:com.netflix.hystrix.HystrixThreadPoolProperties

hystrix:
  threadpool:
    default:
      coreSize: 10 #最大线程数 , 并发执行的最大线程数,默认10
      maxQueueSize: -1 #最大排队长度。默认-1,使用SynchronousQueue。其他值则使用 LinkedBlockingQueue。
      queueSizeRejectionThreshold: 5
      ...

Fallback设置
并发达到 hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests 配置的值时,fallback会被被调用,模式10

通过配置hystrix.command.default.fallback.enabled 开启fallback ,即请求失败尝试走托底

hystrix:
  command:
    default:
      fallback:
       	 enabled: true #请求失败时是否要走托底
         isolation:
           semaphore:
             maxConcurrentRequests: 10 #fallback最大并发数量