8.Hystrix

一、

在这里插入图片描述

二、Hystrix的功能:(隔离、熔断、降级)

1.使用命令模式将所有对外部服务(或依赖关系)的调用包装在HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行;
2.每个依赖都维护着一个线程池(或信号量),线程池被耗尽则拒绝请求(而不是让请求排队)。
3.记录请求成功,失败,超时和线程拒绝。
4.服务错误百分比超过了阈值,熔断器开关自动打开,一段时间内停止对该服务的所有请求。
5.请求失败,被拒绝,超时或熔断时执行降级逻辑。
6.近实时地监控指标和配置的修改。

三、Hystrix整个工作流如下:

1.构造一个 HystrixCommand或HystrixObservableCommand对象,用于封装请求,并在构造方法配置请求被执行需要的参数;
2.执行命令,Hystrix提供了4种执行命令的方法;
3.判断是否使用缓存响应请求,若启用了缓存,且缓存可用,直接使用缓存响应请求。Hystrix支持请求缓存,但需要用户自定义启动;
4.判断熔断器是否打开,如果打开,跳到第8步;
5.判断线程池/队列/信号量是否已满,已满则跳到第8步;
6.执行HystrixObservableCommand.construct()或HystrixCommand.run(),如果执行失败或者超时,跳到第8步;否则,跳到第9步;
7.统计熔断器监控指标;
8.走Fallback备用逻辑
9.返回请求响应

第5步线程池/队列/信号量已满时,还会执行第7步逻辑,更新熔断器统计信息,而第6步无论成功与否,都会更新熔断器统计信息。


四、资源隔离

资源隔离主要指对线程的隔离。Hystrix提供了两种线程隔离方式:线程池和信号量。

通过将发送请求线程与执行请求的线程分离,可有效防止发生级联故障。当线程池或请求队列饱和时,Hystrix将拒绝服务,使得请求线程可以快速失败,从而避免依赖问题扩散。

在这里插入图片描述

五、熔断

现实生活中,可能大家都有注意到家庭电路中通常会安装一个保险盒,当负载过载时,保险盒中的保险丝会自动熔断,以保护电路及家里的各种电器,这就是熔断器的一个常见例子。Hystrix中的熔断器(Circuit Breaker)也是起类似作用,Hystrix在运行过程中会向每个commandKey对应的熔断器报告成功、失败、超时和拒绝的状态,熔断器维护并统计这些数据,并根据这些统计信息来决策熔断开关是否打开。如果打开,熔断后续请求,快速返回。隔一段时间(默认是5s)之后熔断器尝试半开,放入一部分流量请求进来,相当于对依赖服务进行一次健康检查,如果请求成功,熔断器关闭。

熔断器工作的详细过程如下:

第一步,调用allowRequest()判断是否允许将请求提交到线程池

如果熔断器强制打开,circuitBreaker.forceOpen为true,不允许放行,返回。
如果熔断器强制关闭,circuitBreaker.forceClosed为true,允许放行。此外不必关注熔断器实际状态,也就是说熔断器仍然会维护统计数据和开关状态,只是不生效而已。

第二步,调用isOpen()判断熔断器开关是否打开

如果熔断器开关打开,进入第三步,否则继续;
如果一个周期内总的请求数小于circuitBreaker.requestVolumeThreshold的值,允许请求放行,否则继续;
如果一个周期内错误率小于circuitBreaker.errorThresholdPercentage的值,允许请求放行。否则,打开熔断器开关,进入第三步。

第三步,调用allowSingleTest()判断是否允许单个请求通行,检查依赖服务是否恢复

如果熔断器打开,且距离熔断器打开的时间或上一次试探请求放行的时间超过circuitBreaker.sleepWindowInMilliseconds的值时,熔断器器进入半开状态,允许放行一个试探请求;否则,不允许放行。
此外,为了提供决策依据,每个熔断器默认维护了10个bucket,每秒一个bucket,当新的bucket被创建时,最旧的bucket会被抛弃。其中每个blucket维护了请求成功、失败、超时、拒绝的计数器,Hystrix负责收集并统计这些计数器。

六、回退降级

降级,通常指务高峰期,为了保证核心服务正常运行,需要停掉一些不太重要的业务,或者某些服务不可用时,执行备用逻辑从故障服务中快速失败或快速返回,以保障主体业务不受影响。Hystrix提供的降级主要是为了容错,保证当前服务不受依赖服务故障的影响,从而提高服务的健壮性。要支持回退或降级处理,可以重写HystrixCommand的getFallBack方法或HystrixObservableCommand的resumeWithFallback方法。

Hystrix在以下几种情况下会走降级逻辑:

  • 执行construct()或run()抛出异常
  • 熔断器打开导致命令短路
  • 命令的线程池和队列或信号量的容量超额,命令被拒绝
  • 命令执行超时
org.springframework.dao.InvalidDataAccessApiUsageException: MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.; nested exception is redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error. at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:69) at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:42) at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44) at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42) at org.springframework.data.redis.connection.jedis.JedisClusterConnection.convertJedisAccessException(JedisClusterConnection.java:777) at org.springframework.data.redis.connection.jedis.JedisClusterStringCommands.convertJedisAccessException(JedisClusterStringCommands.java:525) at org.springframework.data.redis.connection.jedis.JedisClusterStringCommands.setEx(JedisClusterStringCommands.java:177) at org.springframework.data.redis.connection.DefaultedRedisConnection.setEx(DefaultedRedisConnection.java:308) at org.springframework.data.redis.core.DefaultValueOperations$4.potentiallyUsePsetEx(DefaultValueOperations.java:268) at org.springframework.data.redis.core.DefaultValueOperations$4.doInRedis(DefaultValueOperations.java:261) at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228) at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188) at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96) at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:256) at com.hikvision.ms.spring.starter.autoconfigure.redisson.RedisUtils.set(RedisUtils.java:214) at com.hikvision.ms.mall.oversea.invoice.cache.RedisCacheUtils.putCache(RedisCacheUtils.java:58) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.netflix.hystrix.contrib.javanica.command.MethodExecutionAction.execute(MethodExecutionAction.java:116) at com.netflix.hystrix.contrib.javanica.command.MethodExecutionAction.executeWithArgs(MethodExecutionAction.java:93) at com.netflix.hystrix.contrib.javanica.command.MethodExecutionAction.execute(MethodExecutionAction.java:78) at com.netflix.hystrix.contrib.javanica.command.GenericCommand$1.execute(GenericCommand.java:48) at com.netflix.hystrix.contrib.javanica.command.AbstractHystrixCommand.process(AbstractHystrixCommand.java:145) at com.netflix.hystrix.contrib.javanica.command.GenericCommand.run(GenericCommand.java:45) at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.call(OperatorSubscribeOn.java:100) at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:56) at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:47) at org.springframework.cloud.sleuth.instrument.async.TraceCallable.call(TraceCallable.java:71) at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction.call(HystrixContexSchedulerAction.java:69) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error. at redis.clients.jedis.Protocol.processError(Protocol.java:132) at redis.clients.jedis.Protocol.process(Protocol.java:166) at redis.clients.jedis.Protocol.read(Protocol.java:220) at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:278) at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:196) at redis.clients.jedis.BinaryJedis.setex(BinaryJedis.java:676) at redis.clients.jedis.BinaryJedisCluster$26.execute(BinaryJedisCluster.java:363) at redis.clients.jedis.BinaryJedisCluster$26.execute(BinaryJedisCluster.java:360) at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:106) at redis.clients.jedis.JedisClusterCommand.runBinary(JedisClusterCommand.java:49) at redis.clients.jedis.BinaryJedisCluster.setex(BinaryJedisCluster.java:365) at org.springframework.data.redis.connection.jedis.JedisClusterStringCommands.setEx(JedisClusterStringCommands.java:175) ... 49 common frames omitted 2025-11-21 07:11:37.619 INFO [malloverseainvoice,8b8a8e18e064ba4c,8b8a8e18e064ba4c,false,] 42 --- [heHystrixPool-8] c.h.m.m.o.invoice.cache.RedisCacheUtils : 缓存key:malloverseainvoice_7780337618132992_INVOICE_RETURN_ID_KEY_1放入缓存结果:false 这个是什么原因
11-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值