在分布式系统中,每个服务可能会调用很多其他服务,被调用的那些服务就是依赖服务,有时候某些依赖服务出现故障,调用者就会超时等待或者得到异常响应,当大量的服务请求出现在这个服务时,会发生雪崩效应。
服务雪崩效应是一种因“服务提供者的不可用”(原因)导致“服务调用者不可用”(结果),并将不可用逐渐放大的现象。
形成原因:
服务雪崩的过程可以分为三个阶段:
1、服务提供者不可用(硬件故障,程序bug,网络延迟,缓存击穿);
2、重试机制或访问量激增加大请求流量;
3、服务调用者不可用(同步等待造成资源耗尽)。
解决方案
(1)服务降级
服务降级指的是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
服务降级的方式:
1)延迟服务: 如发表了评论,延迟给用户增加积分,放到缓存,等服务平稳之后再执行。
2)在粒度范围内关闭服务(片段降级或服务功能降级): 如关闭相关文章的推荐,直接关闭推荐区。
3)页面异步请求降级: 如商品详情页上有推荐信息/配送至等异步加载的请求,如果这些信息响应慢或者后端服务有问题,可以进行降级;
4)页面跳转(页面降级): 如可以有相关文章推荐,但是更多的页面则直接跳转到某一个地址。
5)写降级: 比如秒杀抢购,我们可以只进行Cache的更新,然后异步同步扣减库存到DB,保证最终一致性即可,此时可以将DB降级为Cache。
6)读降级: 比如多级缓存模式,如果后端服务有问题,可以降级为只读缓存,这种方式适用于对读一致性要求不高的场景。
(2)服务熔断
熔断是应对微服务雪崩效应的一种链路保护机制,当调用链路的某个微服务不可用或者响应时间太长时,会进行服务熔断,不再有该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
降级的目的在于应对系统自身的故障,而熔断的目的在于应对当前系统依赖的外部系统或者第三方系统的故障。
推荐的熔断器组件如下:
Hystrix
Sentinel
SpringRetry
本文介绍Hystrix。
(3)应用扩容
对流量较大的服务进行多节点部署或者增加内存等硬件资源。
(4)使用缓存
对于读操作频繁的请求,添加缓存预加载。
(5)流控
控制请求流量(限流),或者关闭重试机制。
Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。
Hystrix主 要通过以下几点实现延迟和容错。
(1)包裹请求:使用@HystrixCommand包裹对依赖服务的调用逻辑。
(2)跳闸机制:当某服务的错误率超过一定的阈值时,Hystrix可以跳闸,停止请求该服务一段时间。
(3)资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(舱壁模式)(或者信号量)。如果该线程池已满, 发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
(4)监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
(5)回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员自行提供,例如返回一个缺省值。
(6)自我修复:断路器打开一段时间后,会自动进入“半开”状态。
(1)引入Hystrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
(2)启动类中添加熔断器开启注解@EnableCircuitBreaker
@SpringBootApplication
@EnableDiscoveryClient
//@EnableHystrix // 开启Hystrix功能
@EnableCircuitBreaker // 开启熔断器功能
//@SpringCloudApplication 综合性的注解 @SpringCloudApplication = @SpringBootApplication + @EnableDiscoveryClient + @EnableCircuitBreaker
public class HystrixTestApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixTestApplication.class,args);
}
// 使用RestTemplate模板对象进行远程调用
@Bean
//Ribbon负载均衡
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
(3)定义服务降级处理方法,并在业务方法上使用@HystrixCommand的fallbackMethod属性关联到服务降级处理方法
@GetMapping("/checkStateTimeoutFallback/{userId}")
@HystrixCommand(fallbackMethod = "myFallBack" // 回退方法)
public Integer findResumeOpenStateTimeoutFallback(@PathVariable Long userId) {
String url = "http://lagou-service-resume/resume/openstate/" + userId;
Integer forObject = restTemplate.getForObject(url, Integer.class);
return forObject;
}
/**
* 定义回退方法,返回预设默认值
* 注意:该方法形参和返回值与原始方法保持一致
*/
public Integer myFallBack(Long userId) {
// 兜底数据
return -1;
}
正常调用情况下,请求成功,返回正常数据;停止lagou-service-resume服务,即让远程调用失败,此时,远程服务调用失败了,资源不存在,触发降级 ,Hystrix返回兜底数据。
熔断的实现是在降级的注解上增加熔断条件commandProperties。
熔断条件示例: 20秒内出现3个请求,失败率为30%,就会触发熔断,30秒内不再发送调用
@HystrixCommand(fallbackMethod = "fallback",commandProperties = {
// 条件一: 请求数量达到3个
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "3"),
// 条件二: 每20秒一个判断单位
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_INTERRUPT_ON_TIMEOUT,value = "20000"),
// 条件三: 失败率30%
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "30"),
// 结果: 熔断后, 30秒内不再请求远程服务
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "30000")
})
Command属性主要用来控制HystrixCommand命令的行为,它主要分下面的类别:
1、Execution:用来控制HystrixCommand.run()的执行
execution.isolation.strategy:该属性用来设置HystrixCommand.run()执行的隔离策略。默认为THREAD。
execution.isolation.thread.timeoutInMilliseconds:该属性用来配置HystrixCommand执行的超时时间,单位为毫秒。
execution.timeout.enabled:该属性用来配置HystrixCommand.run()的执行是否启用超时时间。默认为true。
execution.isolation.thread.interruptOnTimeout:该属性用来配置当HystrixCommand.run()执行超时的时候是否要它中断。
execution.isolation.thread.interruptOnCancel:该属性用来配置当HystrixCommand.run()执行取消时是否要它中断。
execution.isolation.semaphore.maxConcurrentRequests:当HystrixCommand命令的隔离策略使用信号量时,该属性用来配置信号量的大小。当最大并发请求达到该设置值时,后续的请求将被拒绝。
2、Fallback:用来控制HystrixCommand.getFallback()的执行
fallback.isolation.semaphore.maxConcurrentRequests:该属性用来设置从调用线程中允许HystrixCommand.getFallback()方法执行的最大并发请求数。当达到最大并发请求时,后续的请求将会被拒绝并抛出异常。
fallback.enabled:该属性用来设置服务降级策略是否启用,默认是true。如果设置为false,当请求失败或者拒绝发生时,将不会调用HystrixCommand.getFallback()来执行服务降级逻辑。
3、Circuit Breaker:用来控制HystrixCircuitBreaker的行为。
circuitBreaker.enabled:确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求。默认为true。
circuitBreaker.requestVolumeThreshold:用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为20的时候,如果滚动时间窗(默认10秒)内仅收到19个请求,即使这19个请求都失败了,断路器也不会打开。
circuitBreaker.sleepWindowInMilliseconds:用来设置当断路器打开之后的休眠时间窗。休眠时间窗结束之后,会将断路器设置为“半开”状态,尝试熔断的请求命令,如果依然时候就将断路器继续设置为“打开”状态,如果成功,就设置为“关闭”状态。
circuitBreaker.errorThresholdPercentage:该属性用来设置断路器打开的错误百分比条件。默认值为50,表示在滚动时间窗中,在请求值超过requestVolumeThreshold阈值的前提下,如果错误请求数百分比超过50,就把断路器设置为“打开”状态,否则就设置为“关闭”状态。
circuitBreaker.forceOpen:该属性默认为false。如果该属性设置为true,断路器将强制进入“打开”状态,它会拒绝所有请求。该属性优于forceClosed属性。
circuitBreaker.forceClosed:该属性默认为false。如果该属性设置为true,断路器强制进入“关闭”状态,它会接收所有请求。如果forceOpen
属性为true,该属性不生效。
4、Metrics:该属性与HystrixCommand和HystrixObservableCommand执行中捕获的指标相关。
metrics.rollingStats.timeInMilliseconds:该属性用来设置滚动时间窗的长度,单位为毫秒。该时间用于断路器判断健康度时需要收集信息的持续时间。断路器在收集指标信息时会根据设置的时间窗长度拆分成多个桶来累计各度量值,每个桶记录了一段时间的采集指标。例如,当为默认值10000毫秒时,断路器默认将其分成10个桶,每个桶记录1000毫秒内的指标信息。
metrics.rollingStats.numBuckets:用来设置滚动时间窗统计指标信息时划分“桶”的数量。默认值为10。
metrics.rollingPercentile.enabled:用来设置对命令执行延迟是否使用百分位数来跟踪和计算。默认为true,如果设置为false,那么所有的概要统计都将返回-1。
metrics.rollingPercentile.timeInMilliseconds:用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
metrics.rollingPercentile.numBuckets:用来设置百分位统计滚动窗口中使用桶的数量。
metrics.rollingPercentile.bucketSize:用来设置每个“桶”中保留的最大执行数。
metrics.healthSnapshot.intervalInMilliseconds:用来设置采集影响断路器状态的健康快照的间隔等待时间。
5、Request Context:涉及HystrixCommand使用HystrixRequestContext的设置。
requestCache.enabled:用来配置是否开启请求缓存。
requestLog.enabled:用来设置HystrixCommand的执行和事件是否打印到日志的HystrixRequestLog中。