一、Hystrix详解
1. Hystrix介绍
在分布式系统中,每个服务都可能会调用很多其他服务,当链路上某个微服务的调用响应的时间过长或者不可用时,就会导致整个系统发生级联故障(雪崩效应)。
Hystrix 可以让我们在分布式系统中对服务间的调用进行控制,加入一些调用延迟或者依赖故障的容错机制,保证一个服务出现故障时,不会导致整个系统出现雪崩效应,以提高分布式系统弹性。
2.服务降级
服务出现故障时,给故障服务降级到事先准备好的故障处理结果,将此结果返回给服务消费者。
例如:在分布式架构中,A服务调用B服务的时候,由于B服务宕机了,导致请求失败,那这个时候我们有几种方式来处理:
1.返回一个备用的数据,比如“系统繁忙”的信息给到用户,这种就是属于降级;
2.发起重试,这就是容错。
降级实际上有很多的方案主要分两大类,主动降级和被动降级:1.主动降级:比如大促的时候关闭非核心服务。
2.被动降级:熔断触发降级、请求超时触发降级、限流触发降级。
3.服务熔断
微服务调用链路中,如果某个目标服务调用比较慢或者大量的超时,这个时候如果触发熔断机制,则可以保证后续的请求不会继续发送到目标服务上,而是直接返回降级的逻辑并且快速释放资源。如果目标服务的情况恢复了,那么熔断机制又会动态进行关闭。
熔断状态
熔断打开:熔断打开后,在此时间内不会对该服务进行调用,而是直接访问降级方法。通过设置熔断时间,当达到该时间后,会尝试恢复该服务。
熔断关闭:熔断关闭代表服务正常,不会干扰正常服务调用。
熔断半开:熔断半开时,请求可以访问服务,若请求正常访问,则熔断会关闭;若请请求不正常,继续熔断,调用降级方法。
4.触发方式
(1)熔断触发降级
对某个服务的调用在一定的时间内(默认10s),发起了超过一定次数请求(默认20次),失败率超过一定值(默认50%),该服务的断路器会打开。返回一个由开发者设定的fallback。
熔断的恢复时间(熔断5s),从熔断开启到后续5s之内的请求,都不会发起到远程服务端。
@RestController
public class Controller {
@HystrixCommand(commandProperties = {
//是否启用熔断器
@HystrixProperty(name="circuitBreaker.enabled",value ="true"),
//断路器的窗口期内触发断路的请求阈值
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "5"),
//断路器的快照时间窗,也叫做窗口期。可以理解为一个触发断路器的周期时间值
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "5000"),
//断路器的窗口期内能够容忍的错误百分比阈值
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50")
},fallbackMethod = "fallback")
@PostMapping("hello")
public String hello() {
return "hello world";
}
}
(2)请求超时触发降级
服务调用超过设置的时间阈值,触发服务降级
@RestController
public class Controller {
@HystrixCommand(fallbackMethod ="timeoutFallback",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000"),
})
@PostMapping("hello")
public String hello() {
return "hello world";
}
}
(3) 限流触发降级
分为信号量隔离和线程池隔离,设置信号量最大并发数,允许通过的线程访问服务执行结束后归还信号量,如果没有信号量触发服务降级
信号量隔离
@RestController
public class Controller {
@HystrixCommand(fallbackMethod = "fallback",
commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"), // 信号量隔离
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQU ESTS, value = "100") // 信号量最大并发数
)
@PostMapping("hello")
public String hello() {
return "hello world";
}
}
线程池隔离
@RestController
public class Controller {
@HystrixCommand(groupKey = "order-service",
commandKey = "queryOrder",
threadPoolKey = "order-service",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),//线程池大小
@HystrixProperty(name = "maxQueueSize", value = "100"),//最大队列长度
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),//线程存活时间
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒绝请求
},
fallbackMethod = "TimeoutHandler")
public String hello() {
return "hello world";
}
}
区别
- 信号量隔离,不会使用Hystrix管理的线程池处理请求。而是使用容器(Tomcat)的线程处理请求逻辑,线程池隔离使用Hystrix管理的线程池。
- 信号量隔离不涉及线程切换,资源调度,上下文的转换等,相对效率高,线程池隔离涉及这些
- 信号量隔离不支持异步处理,不支持超时处理,但是可以传递http header,线程池隔离支持异步和超时,但是无法传递http header
二、面试题合集
1.Hystrix概述与基本原理
(1)什么是Hystrix
Hystrix主要用于实现在分布式系统中的延迟和容错处理,保证一个服务故障不会导致整个系统的服务雪崩。提供了服务降级、服务熔断、服务隔离、服务监控等防雪崩技术。
(2)什么是服务雪崩效应
雪崩效应实在大型互联网项目中,当某个服务发生宕机,调用这个服务的其他服务也会发生宕机,大型项目的微服务之间的调用是互通的,这样就会将服务的不可用逐步扩大到各个其他服务中,从而使整个项目宕机崩溃。
(3)服务降级、服务熔断、服务隔离的区别
服务降级:当客户端请求服务器端的时候,防止客户端一直等待,直接返回一个友好的提示给客户端;
服务熔断:在服务降级上更直接的一种保护方式,当在一个统计时间范围内的请求失败数量达到设定值或者当前的请求错误率达到设定的错误率阈值时开启断路,之后的请求直接走fallback方法,在设定时间后尝试恢复。
服务隔离:为隔离的服务开启一个独立的线程池,在高并发的情况下不会影响其他服务。服务隔离有线程池和信号量两种实现方式,一般使用线程池方式。
2.服务降级
(1)什么是服务降级
服务降级是一种容错机制,它用于处理分布式系统中的部分失败,以保障系统整体的可用性和稳定性。当一个系统的某个组件或依赖的外部服务出现问题,无法正常响应请求时,服务降级可以通过预设的方式,提供一个简化或备用的响应,而不是让整个请求或者流程失败。
(2)服务降级的底层是如何实现的
通过重写HystrixCommand中的getFallback()方法,当Hystrix的run方法或construct执行错误时转而执行getFallback()方法。
3.服务熔断
(1)什么是断路器模式
断路器模式是一种软件设计模式,用于在分布式系统环境中防止级联失败。目的是在系统部分组件发生问题时,快速“断开”故障部分,以保护整个系统的稳定性和不被累进性故障影响。
(2)断路器模式有哪些状态
关闭状态:在这个状态下,断路器允许请求通过,即服务正常调用。断路器监控调用成功和失败的次数。如果失败次数超过设定的阈值,则断路器转为打开状态。
打开状态:一旦断路器打开,所有的请求都会被阻止。在这个状态下,断路器会启动一个计时器。当计时器到达设定的时间阈值后,断路器会转到半开状态去检测服务是否恢复正常。
半开状态:断路器在半开状态下会允许一定数量的请求通过,并检查这些请求是否成功。如果成功的请求达到一定的阈值,认为服务已经恢复,断路器重置并回转到关闭状态,从而再次允许所有请求通过。如果请求依然失败,断路器将返回到打开状态,计时器重置。
4.服务隔离
(1)什么是线程隔离和信号量隔离
线程隔离:线程隔离是Hystrix的默认隔离策略。在这种模式下,服务的调用将在单独的线程上进行。如果下游服务响应缓慢或者出现延迟,这些问题不会直接影响到调用方的线程,即使调用的服务发生阻塞,由于它运行在不同的线程上,调用方的线程可以继续处理其他请求。
信号量隔离:信号量隔离是一种轻量级的隔离策略,它通过限制并发访问的数量来实现隔离。Hystrix使用信号量对特定服务的并发调用进行计数。一旦达到设定的最大并发数,后续的调用将会被立即拒绝,对应的回退逻辑会被执行。信号量隔离不涉及线程切换,对资源的开销小于线程隔离,适合对延迟要求不高且调用复杂度低的服务调用。
(2)如何选择线程隔离和信号量隔离 (线程隔离和信号量隔离的使用场景分别是什么)
线程隔离:即将每个外部调用分配到单独的线程执行,调用者线程不会因为一个外部服务的延迟或失败而被阻塞;即使那个外部服务崩溃也不会导致调用者线程崩溃,但是通常需要更多的系统资源,这个策略经常在以下场合使用:
高延迟敏感性:适用于需要严格控制响应时间和避免长时间阻塞的情况。
远程服务调用:对第三方服务或远程服务的调用,尤其是响应时间无法保证的情况。
容错性要求高:当一个服务的稳定性无法保证,为防止潜在的故障传播到调用者服务。
异步调用:需要执行长时间运算或I/O操作,希望能异步执行并且能够监控和管理这些长时间运行的任务。信号量隔离:信号量隔离使用计数信号量来限制并发访问同一资源的线程数量。这种方法不会启动新的线程,而是在当前线程中执行操作,且并发量超过一定阈值时,新的请求将会被拒绝或者排队。信号量隔离的常见使用场合:
轻量级隔离需求:不需要创建新线程,资源消耗较少。
内部限制:适用于需要保护的资源是在JVM内部的,如数据库连接池大小或内存使用量。
高并发环境:在高并发访问共享资源时,能够通过简单的计数限制来防止资源过载。
简单的流量控制:对网络I/O不敏感的本地或内存中操作的限制。
(3)如何配置Hystrix的隔离策略?
线程池隔离(默认策略):
- 置线程池大小:
- 线程池的核心线程数量:@HystrixProperty(name = "coreSize", value = "10")
- 队列的最大容量:@HystrixProperty(name = "maxQueueSize", value = "20")
- 配置超时:@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") })
信号量隔离:
- 开启信号量隔离策略:@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")
- 配置信号量大小:@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "5")