Hystrix - 阻断器

一、什么是灾难性雪崩效应

造成灾难性雪崩效应的原因,可以简单归结为下述三种:
服务提供者不可用。如:硬件故障、程序BUG、缓存击穿、并发请求量过大等。
重试加大流量。如:用户重试、代码重试逻辑等。
服务调用者不可用。如:同步请求阻塞造成的资源耗尽等。
雪崩效应最终的结果就是:服务链条中的某一个服务不可用,导致一系列的服务不可用,最终造成服务逻辑崩溃。这种问题造成的后果,往往是无法预料的。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、解决灾难性雪崩效应

解决灾难性雪崩效应的方式通常有: 降级、隔离、熔断、请求缓存、请求合并.
添加maven的jar包

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

通常来说,开发的时候,使用ribbon处理服务灾难雪崩效应,开发的成本低。维护成本高。使用feign技术处理服务灾难雪崩效应,开发的成本较高,维护成本低。

降级

当请求超时、资源不足等情况发生时进行服务降级处理,不调用真实服务逻辑,而是使用快速失败(fallback)方式直接返回一个托底数据,保证服务链条的完整,避免服务雪崩。
在main方法上添加注解@EnableCircuitBreaker
在调用application service相关代码中,增加新的方法注解@HystrixCommand,代表当前方法启用Hystrix处理服务雪崩效应。@HystrixCommand注解中的属性:
fallbackMethod - 代表当调用的application service出现问题时,调用哪个fallback快速失败处理方法返回托底数据。

隔离

a. 当服务发生问题时,使用技术手段隔离请求,保证服务调用链的完整。隔离分为线程池隔离和信号量隔离两种实现方式
b. 线程池隔离,就是将并发请求量大的部分服务使用独立的线程池处理,避免因个别服务并发过高导致整体应用宕机
①优点:使用线程池隔离可以完全隔离依赖的服务,请求线程可以快速放回。当线程池出现问题时,线程池是完全隔离状态的,是独立的,不会影响到其他服务的正常执行。当崩溃的服务恢复时,线程池可以快速清理并恢复,不需要相对漫长的恢复等待。独立的线程池也提供了并发处理能力。
②缺点:线程池隔离机制,会导致服务硬件计算开销加大(CPU计算、调度等),每个命令的执行都涉及到排队、调度、上下文切换等,这些命令都是在一个单独的线程上运行的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

线程池隔离的实现方式同样是使用@HystrixCommand注解。相关注解配置属性如下:
groupKey - 分组命名,在application client中会为每个application service服务设置一个分组,同一个分组下的服务调用使用同一个线程池。默认值为this.getClass().getSimpleName();
commandKey - Hystrix中的命令命名,默认为当前方法的方法名。可省略。用于标记当前要触发的远程服务是什么。
threadPoolKey - 线程池命名。要求一个应用中全局唯一。多个方法使用同一个线程池命名,代表使用同一个线程池。默认值是groupKey数据。
threadPoolProperties - 用于为线程池设置的参数。其类型为HystrixProperty数组。常用线程池设置参数有:
coreSize - 线程池最大并发数,建议设置标准为:requests per second at peak when healthy * 99th percentile latency in second + some breathing room。 即每秒最大支持请求数*(99%平均响应时间 + 一定量的缓冲时间)。如:每秒可以处理请求数为1000,99%的响应时间为60ms,自定义提供缓冲时间为12ms,那么结果是 1000*(0.060+0.012) = 72。
maxQueueSize - BlockingQueue的最大长度,默认值为-1,即不限制。如果设置为正数,等待队列将从同步队列SynchronousQueue转换为阻塞队列LinkedBlockingQueue。
queueSizeRejectionThreshold - 设置拒绝请求的临界值。默认值为5。此属性是配合阻塞队列使用的,也就是不适用maxQueueSize=-1的情况。是用于设置阻塞队列限制的,如果超出限制,则拒绝请求。不常用。
keepAliveTimeMinutes - 线程存活时间,单位是分钟。默认值为1。
execution.isolation.thread.timeoutInMilliseconds - 超时时间,默认为1000ms。当请求超时自动中断。
execution.isolation.thread.interruptOnTimeout - 是否开启超时中断。默认为TRUE。和上一个属性配合使用。

c. 信号量隔离

设置一个并发处理的最大极值。当并发请求数超过极值时,通过fallback返回托底数据,保证服务完整性。
在这里插入图片描述

信号量隔离同样通过@HystrixCommand注解配置,常用注解属性有:
commandProperty - 配置信号量隔离具体数据。属性类型为HystrixProperty数组,常用配置内容如下:
execution.isolation.strategy - 设置隔离方式,默认为线程池隔离。可选值只有THREAD和SEMAPHORE。
execution.isolation.semaphore.maxConcurrentRequests - 最大信号量并发数,默认为10。

线程池隔离:请求并发大,耗时较长(一般都是计算大,服务链长或访问数据库)时使用线程池隔离。可以尽可能保证外部容器(如Tomcat)线程池可用,不会因为服务调用的原因导致请求阻塞等待。
信号量隔离:请求并发大,耗时短(计算小,服务链段或访问缓存)时使用信号量隔离。因为这类服务的响应快,不会占用外部容器(如Tomcat)线程池太长时间,减少线程的切换,可以避免不必要的开销,提高服务处理效率。

熔断

当一定时间内,异常请求比例(请求超时、网络故障、服务异常等)达到阀值时,启动熔断器,熔断器一旦启动,则会停止调用具体服务逻辑,通过fallback快速返回托底数据,保证服务链的完整。
熔断有自动恢复机制,如:当熔断器启动后,每隔5秒,尝试将新的请求发送给服务提供者,如果服务可正常执行并返回结果,则关闭熔断器,服务恢复。如果仍旧调用失败,则继续返回托底数据,熔断器持续开启状态。
在这里插入图片描述

熔断的实现是在调用远程服务的方法上增加@HystrixCommand注解。当注解配置满足则开启或关闭熔断器。
注解属性描述:
CIRCUIT_BREAKER_ENABLED (“circuitBreaker.enabled”): 是否开启熔断策略。默认值为true。
CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD (“circuitBreaker.requestVolumeThreshold”): 10ms内,请求并发数超出则触发熔断策略。默认值为20。
CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS (“circuitBreaker.sleepWindowInMilliseconds”): 当熔断策略开启后,延迟多久尝试再次请求远程服务。默认为5秒。
CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE(“circuitBreaker.errorThresholdPercentage”): 10ms内,出现错误的请求百分比达到限制,则触发熔断策略。默认为50%。
CIRCUIT_BREAKER_FORCE_OPEN(“circuitBreaker.forceOpen”): 是否强制开启熔断策略。即所有请求都返回fallback托底数据。默认为false。
CIRCUIT_BREAKER_FORCE_CLOSED(“circuitBreaker.forceClosed”): 是否强制关闭熔断策略。即所有请求一定调用远程服务。默认为false。

请求缓存

缓存是指请求缓存。通常意义上说,就是将同样的GET请求结果缓存起来,使用缓存机制(如redis、mongodb)提升请求响应效率。
使用请求缓存时,需要注意非幂等性操作对缓存数据的影响。
请求缓存是依托某一缓存服务来实现的。在案例中使用redis作为缓存服务器,那么可以使用spring-data-redis来实现redis的访问操作。需要在application client相关工程中导入下述依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在Spring Cloud应用中,启用spring对cache的支持,需要在启动类中增加注解@EnableCaching,此注解代表当前应用开启spring对cache的支持。简言之就是使spring-data-redis相关的注解生效,如:@CacheConfig、@Cacheable、@CacheEvict等。
spring cloud会检查每个幂等性请求,如果请求完全相同(路径、参数等完全一致),则首先访问缓存redis,查看缓存数据,如果缓存中有数据,则不调用远程服务application service。如果缓存中没有数据,则调用远程服务,并将结果缓存到redis中,供后续请求使用。
如果请求是一个非幂等性操作,则会根据方法的注解来动态管理redis中的缓存数据,避免数据不一致。
注意:使用请求缓存会导致很多的隐患,如:缓存管理不当导致的数据不同步、问题排查困难等。在商业项目中,解决服务雪崩效应不推荐使用请求缓存。

请求合并

在一定时间内,收集一定量的同类型请求,合并请求需求后,一次性访问服务提供者,得到批量结果。这种方式可以减少服务消费者和服务提供者之间的通讯次数,提升应用执行效率。
在这里插入图片描述
在这里插入图片描述>>>什么情况下使用请求合并:
在微服务架构中,我们将一个项目拆分成很多个独立的模块,这些独立的模块通过远程调用来互相配合工作,但是,在高并发情况下,通信次数的增加会导致总的通信时间增加,同时,线程池的资源也是有限的,高并发环境会导致有大量的线程处于等待状态,进而导致响应延迟,为了解决这些问题,我们需要来了解Hystrix的请求合并。
通常来说,服务链条超出4个,不推荐使用请求合并。因为请求合并有等待时间。
请求合并的缺点:
设置请求合并之后,本来一个请求可能5ms就搞定了,但是现在必须再等10ms看看还有没有其他的请求一起的,这样一个请求的耗时就从5ms增加到15ms了,不过,如果我们要发起的命令本身就是一个高延迟的命令,那么这个时候就可以使用请求合并了,因为这个时候时间窗的时间消耗就显得微不足道了,另外高并发也是请求合并的一个非常重要的场景。
使用注解@HystrixCollapser来描述需要合并请求的方法,并提供合并方法使用注解@HystrixCommand来描述。当合并条件(@HystrixCollapser)满足时,会触发合并方法(@HystrixCommand)来调用远程服务并得到结果。
@HystrixCollapser注解介绍:
此注解描述的方法,返回值类型必须是java.util.concurrent.Future类型的。代表方法为异步方法。
@HystrixCollapser注解的属性:
batchMethod - 请求合并方法名。
scope - 请求合并方式。可选值有REQUEST和GLOBAL。REQUEST代表在一个request请求生命周期内的多次远程服务调用请求需要合并处理,此为默认值。GLOBAL代表所有request线程内的多次远程服务调用请求需要合并处理。
timerDelayInMilliseconds - 多少时间间隔内的请求进行合并处理,默认值为10ms。建议设置时间间隔短一些,如果单位时间并发量不大,并没有请求合并的必要。
maxRequestsInBatch - 设置合并请求的最大极值,也就是timerDelayInMilliseconds时间内,最多合并多少个请求。默认值是Integer.MAX_VALUE。

Hystrix的全局相关配置


# hystrix.command.default和hystrix.threadpool.default中的default为默认CommandKey,CommandKey默认值为服务方法名,在properties配置中配置格式混乱,如果需要为每个方法设置不同的容错规则,建议使用yml文件配置。

# Command Properties

# Execution相关的属性的配置:
# 隔离策略,默认是Thread, 可选Thread|Semaphore
hystrix.command.default.execution.isolation.strategy=THREAD
#命令执行超时时间,默认1000ms,只在线程池隔离中有效。
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
# 执行是否启用超时,默认启用true,只在线程池隔离中有效。
hystrix.command.default.execution.timeout.enabled=true
# 发生超时是是否中断,默认true,只在线程池隔离中有效。
hystrix.command.default.execution.isolation.thread.interruptOnTimeout=true
# 最大并发请求数,默认10,该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效。如果达到最大并发请求数,请求会被拒绝。理论上选择semaphore的原则和选择thread一致,但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),否则的话应该用thread。semaphore应该占整个容器(tomcat)的线程池的一小部分。
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10
# 如果并发数达到该设置值,请求会被拒绝和抛出异常并且fallback不会被调用。默认10。只在信号量隔离策略中有效。
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=10

# Fallback相关的属性
# 当执行失败或者请求被拒绝,是否会尝试调用fallback方法 。默认true
hystrix.command.default.fallback.enabled=true

# Circuit Breaker相关的属性
# 是否开启熔断器。默认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毫秒内都会拒绝远程服务调用,也就是5000毫秒后才会重试远程服务调用。默认5000
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
# 错误比率阀值,如果错误率&gt;=该值,circuit会被打开,并短路所有请求触发fallback。默认50
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
# 强制打开熔断器
hystrix.command.default.circuitBreaker.forceOpen=false
# 强制关闭熔断器
hystrix.command.default.circuitBreaker.forceClosed=false

# ThreadPool 相关参数
# 并发执行的最大线程数,默认10
hystrix.threadpool.default.coreSize=10
# BlockingQueue的最大队列数,当设为-1,会使用SynchronousQueue,值为正时使用LinkedBlcokingQueue。该设置只会在初始化时有效,之后不能修改threadpool的queue size,除非reinitialising thread executor。默认-1。
hystrix.threadpool.default.maxQueueSize=-1
# 即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝。
hystrix.threadpool.default.queueSizeRejectionThreshold=20
# 线程存活时间,单位是分钟。默认值为1。
hystrix.threadpool.default.keepAliveTimeMinutes=1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值