SpringCloud (五) --------- Hystrix


一、Hystrix 介绍

Hystrix 被称为熔断器,它是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多服务之间通过远程调用实现信息交互,调用时不可避免会出现调用失败,比如超时、异常等原因导致调用失败,Hystrix 能够保证在一个服务出问题的情况下,不会导致整体服务失败,避免级联故障(服务雪崩),以提高分布式系统的弹性。

1. 什么是熔断器

熔断器也有叫断路器,他们表示同一个意思,最早来源于微服务之父 Martin Fowler 的论文 CircuitBreaker 一文。“熔断器” 本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,能够及时切断故障电路,防止发生过载、发热甚至起火等严重后果。
所以当某个服务单元发生故障之后,通过断路器的故障监控 (类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应 (FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

在这里插入图片描述
比如:

比如电商中的用户下订单,我们有两个服务,一个下订单服务,一个减库存服务, 当用户下订单时调用下订单服务,然后下订单服务又调用减库存服务,如果减库存服务响应延迟或者没有响应,则会造成下订单服务的线程挂起等待,如果大量的用户请求下订单,或导致大量的请求堆积,引起下订单服务也不可用,如果还有另外一个服务依赖于订单服务,比如用户服务,它需要查询用户订单,那么用户服务查询订单也会引起大量的延迟和请求堆积,导致用户服务也不可用。所以在微服务架构中,很容易造成服务故障的蔓延,引发整个微服务系统瘫痪不可用。

2. Hystrix 原理

Spring Cloud Hystrix 实现了熔断器、线程隔离等一系列服务保护功能。该功能也是基于Netflix 的开源框架 Hystrix 实现的,该框架的目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。(Spring Cloud Hystrix 对 Netflix Hystrix 做了一个 starter)。

程序BUG、数据不匹配、响应时间过长、服务不可用等等…都可能导致服务雪崩。

3. Hystrix 提供解决方案

针对上面的问题,Hystrix 提供了 :

  • 熔断降级
  • 请求限流

二、Hystrix 熔断降级

服务降级是指当某个微服务响应时间过长,发生异常,或者服务不可用了,我们不能把错误信息返回回来,或者让它一直卡在那里,所以要准备一个对应的策略 (一个方法),当发生这种问题时,我们直接调用这个备用的方法来快速返回一个默认的结果,让请求得到快速响应,而不是一直卡在那里。

操作步骤

在 Spring Cloud 中使用熔断器 Hystrix 是非常简单和方便的,只需要简单几步即可:

A、添加依赖

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

B、在入口类加上注解

在入口类中使用 @EnableCircuitBreaker 注解或 @EnableHystrix 开启断路器功能,也可以使用一个名为 @SpringCloudApplication 的注解代替主类上的三个注解。

C、在调用远程服务的方法上添加注解

@HystrixCommand(fallbackMethod = "fallback") Hystrix 默认超时时间是 1000 毫秒,如果你后端的响应超过此时间,就会触发断路器。

修改 Hystrix 的默认超时时间:

@RequestMapping("/cloud/goodsHystrix")
@HystrixCommand(fallbackMethod="fallback", commandProperties={@HystrixProperty(name="execution.timeout.enabled", value="true"), @HystrixProperties(name="execution.isolation.thread.timeout.InMilliseconds", value="5000")})
public ResulutObject goodsHystrix() {

}

或在配置文件中进行配置:

ribbon.ReadTimeout=6000
ribbon.ConnectTimeout=3000
hystrix.command.default.execution.timeout.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000

这里有个坑需要注意一下:

如果 hystrix.command.default.execution.timeout.enabled 为 true,则会有两个执行方法超时的配置,一个就是 Ribbon 的 ReadTimeout,一个就是熔断器 hystrix 的timeoutInMilliseconds,此时谁的值小谁生效。

如果 hystrix.command.default.execution.timeout.enabled 为 false,则熔断器不进行超时熔断,而是根据 Ribbon 的 ReadTimeout 抛出的异常而熔断,也就是取决于 Ribbon 的ConnectTimeout,配置的是请求服务的超时时间,除非服务找不到,或者网络原因,这个时间才会生效。

三、Hystrix 的异常处理

我们在调用服务提供者时,服务提供者可能抛出异常,我们自己也可能抛异常,默认情况下方法抛了异常会自动进行服务降级,交给服务降级中的方法去处理。

当我们自己发生异常后,只需要在服务降级方法中添加一个 Throwable 类型的参数就能够获取到抛出的异常的类型,如下:

public ResultObject fallback(Throwable throwable) {
    System.out.println(throwable.getMessage());
    return new ResultObject(Constant.ONE, "服务降级");
}

当然远程服务发生了异常也可以获取到异常信息。

如果远程服务有一个异常抛出后我们不希望进入到服务降级方法中去处理,而是直接将异常抛给用户,那么我们可以在 @HystrixCommand 注解中添加忽略异常,如下:

@HystrixCommand(fallbackMethod="fallback", ignoreExceptions=Throwable.class)

降级是作用?

1、可以监听你的请求有没有超时。(默认是1秒,时间可以改)

2、异常或报错了可以快速让请求返回,不会一直等待。(避免线程累积)

3、当的系统马上迎来大量的并发(双十一秒杀这种或者促销活动),此时如果系统承载不了这么大的并发时,可以考虑先关闭一些不重要的微服务(在降级方法中返回一个比较友好的信息),把资源让给核心微服务,待高峰流量过去,再开启回来。

四、Hystrix 限流

限流有很多方案:

1、Nginx
2、Redis + Lua
3、Sentinel
4、基于限流算法自己实现 (令牌桶、漏桶算法)

Hystrix 限流就是限制你某个微服务的使用量 (可用线程数、信号量)

Hystrix 通过线程池的方式来管理微服务的调用,它默认是一个线程池(大小10个) 管理你的所有微服务,你可以给某个微服务开辟新的线程池:

@RequestMapping("/cloud/goodsHystrix2")
@HystrixCommand(fallbackMethod = "fallback", threadPoolKey="my", threadPoolProperties={@HystrixProperties(name="coreSize", value="2"), @HystrixProperty(name="maxQueueSize", value="1")})
public ResultObject myHystrix() throws InterruptedException {}

threadPoolKey 是线程池唯一标识, Hystrix 会使用该标识来计数,看线程占用是否超过了, 超过了就会直接降级该次调用。这里 coreSize 给他值为 2。那么假设你这个方法调用时间是 1s 执行完, 那么在 1s 内如果有超过 2 个请求进来的话,剩下的请求则全部降级。
其中 maxQueueSize 是一个线程队列,里面只能放一个请求线程,本来线程数有2个,队列里面允许放一个,那么总共只能有3个请求线程执行,如果超过了就会限流。

五、Feign 整合 Hystrix

Feign 默认是支持 Hystrix 的, 但是在Spring cloud Dalston 版本之后就默认关闭了, 因为业务需求不一定要使用。

所以现在要使用首先得打开他,在application.properties文件加上如下配置:

feign.hystrix.enabled=true

加上配置之后降级方法的写法:

@FeignClient(value="springcloud-service-goods"), fallback=GoodsRemoteClientFallBack.class)
public interface GoodsRemoteClient {
    @RequestMapping("/service/goods")
    public ResultObject goods();
}

在 feign 客户端的注解上 有个属性叫 fallback 然后指向一个类

GoodsRemoteClientFallBack 类:

@Component
public class GoodsRemoteClientFallBack implements GoodsRemoteClient {
   @Override
   public ResultObject goods() {
       return new ResultObject(Constant.One, "feign 服务调用降级");
   }
}

如此方法降级便可以了。

当然如果需要拿到具体的服务错误信息,那么可以这样:

客户端指定一个 fallbackFactory 即可:

@FeignClient(value="springcloud-service-goods"),
fallbackFactory=GoodsRemoteClientFallBackFactory.class)
public interface GoodsRemoteClient {
	@RequestMapping("/service/goods")
	public ResultObject goods();
}
@Component 
public class GoodsRemoteClientFallBackFactory implements FallbackFactory<GoodsRemoteClient> {
   @Override
   public GoodsRemoteClient create(Throwable throwable) {
      return new GoodsRemoteClient() {
          @Override
          public ResultObject goods() {
          	 String message = throwable.getMessage();
          	 System.out.println("feign 远程调用异常:" + message);
          	 return new ResultObject();
          } 	
      }  
   };
}

这个 message 就是错误信息,至此,就完成了 Feign 与 Hystrix 的整合。

Spring Cloud Feign 超时时间设置

Feign 调用服务的默认时长是 1 秒钟,也就是如果超过 1 秒没连接上或者超过 1 秒没响应,那么会相应的报错。而实际情况是因为业务的不同可能出现超出 1 秒的情况,这时我们需要调整超时时间。

Feign 的负载均衡底层用的就是 Ribbon。

在 application.properties 中添加如下配置,超过 5 秒没连接上报连接超时,如果超过 5 秒没有响应,报请求超时。

#参考RibbonClientConfiguration
#请求连接的超时时间 默认的时间为 1 秒
ribbon.ConnectTimeout=5000
#请求处理的超时时间
ribbon.ReadTimeout=5000

Ribbon 还有 MaxAutoRetries 对当前实例的重试次数 MaxAutoRetriesNextServer 对切换实例的重试次数。如果 Ribbon 的 ReadTimeout 超时,或者ConnectTimeout 连接超时,会进行重试操作由于 Ribbon 的重试机制,通常熔断 Hystrix 的超时时间需要配置的比 ReadTimeout 长,ReadTimeout 比 ConnectTimeout 长,否则还未重试,就熔断了。

为了确保重试机制的正常运作,理论上 (以实际情况为准) 建议 Hystrix 的超时时间为:(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout

六、Hystrix 相关配置

1. Excetion 属性相关配置

# 隔离策略, 默认是 Thread, 可选 Thread | Semaphore(信号量)
hystrix.command.default.execution.isolation.strategy=Thread

# 命令执行超时时间, 默认是 1000ms
hystrix.command.default.execution.isolation.thread.timeoutMilliseconds=1000

# 执行是否启用超时, 默认启用 true
hystrix.command.default.execution.timeout.enabled=true

#最大并发请求数,默认10,该参数当使用 ExecutionIsolationStrategy.SEMAPHORE 策略时才有效。如果达到最大并发请求数,请求会被拒绝. 理论上选择 semaphore size 的原则和选择 thread size 一致,但选用 semaphore 时每次执行的单元要比较小且执行速度快(ms级别), 否则的话应该用 thread.semaphore 应该占整个容器 (Tomcat) 的线程池的一小部分.
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10

2. Fallback 属性相关配置

这些参数可以应用于 Hystrix 的 THREAD 和 SEMAPHORE 策略

#如果并发数达到该设置值, 请求会被拒绝和抛出异常并且 fallback 不会被调用, 默认10
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequest=10

#当执行失败或者请求被拒绝, 是否会尝试调用hystrixCommand.getFallback(),默认true
hystrix.command.default.fallback.enabled=true

3. Circuit Breaker 属性相关配置

# 用来跟踪 circuit 的健康性, 如果未达标则让 request 短路, 默认 true
hystrix.command.default.circuitBreaker.enabled=true

# 一个 rolling window 内最小的请求数, 如果设为 20, 那么当一个rolling window的时间内 (比如说 1 个 rolling window 是10秒) 收到 19 个请求, 即使 19 个请求都失败, 也不会触发 circuit break. 默认20
hystrix.command.default.circuitBreaker.requestVolumeThreshold=20

#触发短路的时间值, 当该值设为 5000 时, 则当触发 circuit break 后的 5000 毫秒内都会拒绝 request, 也就是 5000 毫秒后才会关闭 circuit.默认5000
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000

#错误比率阀值, 如果错误率>=该值, circuit会被打开,并短路所有请求触发fallback.默认 50
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50

#强制打开熔断器, 如果打开这个开关, 那么拒绝所有 request, 默认false
hystrix.command.default.circuitBreaker.forceOpen=false 

#强制关闭熔断器 如果这个开关打开,circuit将 一直关闭且忽略circuitBreaker.errorThresholdPercentage
hystrix.command.default.circuitBreaker.forceClosed=false

4. Metrics 属性相关配置

#设置统计的时间窗口值的, 毫秒值, circuit break 的打开会根据 1 个 rolling window 的统计来计算。若 rolling window 被设为 10000 毫秒, 则 rolling window 会被分成 n 个 buckets, 每个 bucket 包含 success, failure, timeout, rejection的次数的统计信息。默认10000
hystrix.command.default.metrics.rollingStats.timeInMilliseconds=10000

#设置一个 rolling window 被划分的数量, 若 numBuckets=10,rolling window=10000, 那么一个bucket的时间即1秒, 必须符合 rolling window%numberBuckets == 0.默认10
hystrix.command.default.metrics.rollingStats.numBuckets=10 

#执行时是否 enable 指标的计算和跟踪, 默认true
hystrix.command.default.metrics.rollingPercentile.enabled=true

#设置rolling percentile window的时间,默认60000
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds=60000
 
#设置rolling percentile window 的 numberBuckets. 逻辑同上.默认 6
hystrix.command.default.metrics.rollingPercentile.numBuckets=6

#如果bucket size=100, window =10s, 若这 10s 里有 500 次执行, 只有最后100次执行会被统计到bucket里去。增加该值会增加内存开销以及排序的开销.默认100
hystrix.command.default.metrics.rollingPercentile.bucketSize=100 

#记录health 快照(用 来统计成功和错误绿)的间隔,默认500ms
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds 

5. Request Context 属性相关配置

#默认true, 需要重载getCacheKey(), 返回 null 时不缓存
hystrix.command.default.requestCache.enabled=true

#记录日志到 HystrixRequestLog, 默认true
hystrix.command.default.requestLog.enabled=true 

6. Collapser Properties 属性相关配置

#单次批处理的最大请求数, 达到该数量触发批处理, 默认 Integer.MAX_VALUE
hystrix.collapser.default.maxRequestsInBatch=Integer.MAX_VALUE
#触发批处理的延迟,也可以为创建批处理的时间+该值,默认10
hystrix.collapser.default.timerDelayInMilliseconds=10 
#是否对 HystrixCollapser.execute() and HystrixCollapser.queue()的cache,默认true
hystrix.collapser.default.requestCache.enabled=true

7. ThreadPool 相关参数

线程数默认值 10 适用于大部分情况 (有时可以设置得更小) ,如果需要设置得更大,那有个基本得公式可以 follow:

requests per second at peak when healthy × 99th percentile latency inseconds + some breathing room

即每秒最大支撑的请求数 (99%平均响应时间 + 缓存值)

比如:每秒能处理 1000 个请求,99% 的请求响应时间是 60ms,那么公式是: 1000 (0.060+0.012) 基本得原则时保持线程池尽可能小,他主要是为了释放压力,防止资源被阻塞。 当一切都是正常的时候,线程池一般仅会有 1 到 2 个线程激活来提供服务。

# 并发执行的最大线程数,默认10
hystrix.threadpool.default.coreSize=10

#BlockingQueue的最大队列数,当设为-1,会使用 SynchronousQueue,值为正时使用LinkedBlcokingQueue。该设置只会在初始化时有效,之后不能修改 threadpool 的 queue size,除非 reinitialising thread executor。默认 -1。
hystrix.threadpool.default.maxQueueSize=-1

#即使maxQueueSize没有达到,达到 queueSizeRejectionThreshold该值后,请求也会被拒绝。因为 maxQueueSize 不能被动态修改,这个参数将允许我们动态设置该值。if maxQueueSize == -1,该字段将不起作用。 
hystrix.threadpool.default.queueSizeRejectionThreshold

#如果corePoolSize和maxPoolSize设成一样(默认实现)该设置无效。如果通过 plugin (https://github.com/Netflix/Hystrix/wiki/Plugins)使用自定义 实现,该设置才有用,默认1.
hystrix.threadpool.default.keepAliveTimeMinutes=1

#线程池统计指标的时间, 默认 10000
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds=10000


#将 rolling window 划分为 n 个 buckets, 默认10
hystrix.threadpool.default.metrics.rollingStats.numBuckets=10 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在森林中麋了鹿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值