开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情
概述
在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性。
服务降级
Hystrix 会在以下场景下进行服务降级处理:
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满
我们可以通过重写 HystrixCommand 的 getFallBack() 方法或 HystrixObservableCommand 的 resumeWithFallback() 方法,使服务支持服务降级。
Hystrix 服务降级 FallBack 既可以放在服务端进行,也可以放在客户端进行。
服务端 服务降级
代码示例
pom文件
<!--hystrix 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
Service层添加注解,使单个方法有熔断效果
Service:
//一旦该方法失败并抛出了异常信息后,会自动调用 @HystrixCommand 注解标注的 fallbackMethod 指定的方法
@HystrixCommand(fallbackMethod = "dept_TimeoutHandler",
commandProperties =
//规定 5 秒钟以内就不报错,正常运行,超过 5 秒就报错,调用指定的方法
{@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")})
@Override
public String deptInfo_Timeout(Integer id) {
int outTime = 6;
try {
TimeUnit.SECONDS.sleep(outTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " deptInfo_Timeout,id: " + id + " 耗时: " + outTime;
}
/ 当服务出现故障后,调用该方法给出友好提示
public String dept_TimeoutHandler(Integer id) {
return "系统繁忙请稍后再试!"+"线程池:" + Thread.currentThread().getName() + " deptInfo_Timeout,id: " + id;
}
启动类上添加注解 @EnableCircuitBreaker 激活熔断器功能
客户端服务降级
当客户端调用的服务端的服务不可用时,客户端直接进行服务降级处理,避免其线程被长时间、不必要地占用。
代码示例
服务端 配置文件:
feign:
hystrix:
enabled: true #开启客户端 hystrix
客户端 配置文件:
######################### Ribbon 客户端超时控制 ###################################
ribbon:
ReadTimeout: 6000 #建立连接所用的时间,适用于网络状况正常的情况下,两端两端连接所用的时间
ConnectionTimeout: 6000 #建立连接后,服务器读取到可用资源的时间
######################配置请求超时时间##########################
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 7000
####################配置具体方法超时时间 为 3 秒########################
DeptHystrixService#deptInfo_Timeout(Integer):
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
客户端 Service:
//在客户端进行降级
@RequestMapping(value = "/consumer/dept/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "dept_TimeoutHandler") //为该请求指定专属的回退方法
public String deptInfo_Timeout(@PathVariable("id") Integer id) {
String s = deptHystrixService.deptInfo_Timeout(id);
log.info(s);
return s;
}
// deptInfo_Timeout方法的 专用 fallback 方法
public String dept_TimeoutHandler(@PathVariable("id") Integer id) {
log.info("deptInfo_Timeout 出错,服务已被降级!");
return "服务端系统繁忙,请稍后再试!(客户端 deptInfo_Timeout 专属的回退方法触发)";
}
全局降级方法
代码示例
Controller层添加 @DefaultProperties(defaultFallback = "deptGlobalFallbackMethod") 指定 deptGlobalFallbackMethod 的全局回方法
deptGlobalFallbackMethod 方法写在Controller层
/**
* 全局的 fallback 方法,
* 回退方法必须和 hystrix 的执行方法在相同类中
* @DefaultProperties(defaultFallback = "dept_Global_FallbackMethod") 类上注解,请求方法上使用 @HystrixCommand 注解
*/
public String deptGlobalFallbackMethod() {
return "运行出错或服务端系统繁忙,请稍后再试!(客户端全局回退方法触发,)";
}
所有的Service层 - 业务方法上都标注 @HystrixCommand 注解
//在客户端进行降级
@RequestMapping(value = "/consumer/dept/hystrix/timeout/{id}")
@HystrixCommand
public String deptInfoTimeout(@PathVariable("id") Integer id) {
String s = deptHystrixService.deptInfoTimeout(id);
log.info(s);
return s;
}
注意:全局降级方法的优先级较低,只有业务方法没有指定其降级方法时,服务降级时才会触发全局回退方法。若业务方法指定它自己的回退方法,那么在服务降级时,就只会直接触发它自己的回退方法,而非全局回退方法。
解耦降级逻辑
代码示例
新建 DeptHystrixService 接口的实现类 DeptHystrixFallBackService,统一为 DeptHystrixService 中的方法提供服务降级处理
@Component
public class DeptHystrixFallBackService implements DeptHystrixService {
@Override
public String deptInfoOk(Integer id) {
return "deptInfoOk - 解耦方法---";
}
@Override
public String deptInfoTimeout(Integer id) {
return "deptInfoTimeout - 解耦方法---";
}
}
在调用分镜的地方添加 fallback 属性,属性为 DeptHystrixFallBackService.class
@Component
@FeignClient(value = "MICROSERVICECLOUDPROVIDERDEPTHYSTRIX", fallback = DeptHystrixFallBackService.class)
public interface DeptHystrixService {
@RequestMapping(value = "/dept/hystrix/ok/{id}")
public String deptInfo_Ok(@PathVariable("id") Integer id);
@RequestMapping(value = "/dept/hystrix/timeout/{id}")
public String deptInfo_Timeout(@PathVariable("id") Integer id);
}
服务熔断
熔断机制是为了应对雪崩效应而出现的一种微服务链路保护机制。
当微服务系统中的某个微服务不可用或响应时间太长时,为了保护系统的整体可用性,熔断器会暂时切断请求对该服务的调用,并快速返回一个友好的错误响应。这种熔断状态不是永久的,在经历了一定的时间后,熔断器会再次检测该微服务是否恢复正常,若服务恢复正常则恢复其调用链路。
熔断状态
在熔断机制中涉及了三种熔断状态:
- 熔断关闭状态(Closed):当服务访问正常时,熔断器处于关闭状态,服务调用方可以正常地对服务进行调用。
- 熔断开启状态(Open):默认情况下,在固定时间内接口调用出错比率达到一个阈值(例如 50%),熔断器会进入熔断开启状态。进入熔断状态后,后续对该服务的调用都会被切断,熔断器会执行本地的降级(FallBack)方法。
- 半熔断状态(Half-Open): 在熔断开启一段时间之后,熔断器会进入半熔断状态。在半熔断状态下,熔断器会尝试恢复服务调用方对服务的调用,允许部分请求调用该服务,并监控其调用成功率。如果成功率达到预期,则说明服务已恢复正常,熔断器进入关闭状态;如果成功率仍旧很低,则重新进入熔断开启状态。
熔断状态转化关系
Hystrix 实现熔断机制
Hystrix 会监控微服务间调用的状况,当失败调用到一定比例时(例如 5 秒内失败 20 次),就会启动熔断机制。
Hystrix 实现服务熔断的步骤如下:
- 当服务的调用出错率达到或超过 Hystix 规定的比率(默认为 50%)后,熔断器进入熔断开启状态。
- 熔断器进入熔断开启状态后,Hystrix 会启动一个休眠时间窗,在这个时间窗内,该服务的降级逻辑会临时充当业务主逻辑,而原来的业务主逻辑不可用。
- 当有请求再次调用该服务时,会直接调用降级逻辑快速地返回失败响应,以避免系统雪崩。
- 当休眠时间窗到期后,Hystrix 会进入半熔断转态,允许部分请求对服务原来的主业务逻辑进行调用,并监控其调用成功率。
- 如果调用成功率达到预期,则说明服务已恢复正常,Hystrix 进入熔断关闭状态,服务原来的主业务逻辑恢复;否则 Hystrix 重新进入熔断开启状态,休眠时间窗口重新计时,继续重复第 2 到第 5 步。
代码示例
Service层
@Override
@HystrixCommand(fallbackMethod = "deptCircuitBreakerFallback", commandProperties = {
//以下参数在 HystrixCommandProperties 类中有默认配置
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), //是否开启熔断器
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "1000"), //统计时间窗
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), //统计时间窗内请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), //休眠时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"), //在统计时间窗口期以内,请求失败率达到 60% 时进入熔断状态
})
public String deptCircuitBreaker(Integer id) {
if (id < 0) {
//当传入的 id 为负数时,抛出异常,调用降级方法
throw new RuntimeException("可爱小刘提醒您,id 不能是负数!");
}
String serialNum = UUID.randomUUID().toString();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号为:" + serialNum;
}
/**
* @Author: Take-off
* @Description: //TODO deptCircuitBreaker 的降级方法
* @Date: 2:06 PM 2022/11/2
**/
public String deptCircuitBreakerFallback(Integer id) {
return "可爱小刘提醒您,id 不能是负数,请稍后重试! 别tm输入了 id:" + id;
}
当服务调用正确率上升到一定的利率后,Hystrix 进入熔断关闭状态。
Hystrix 故障监控
Hystrix 还提供了准实时的调用监控(Hystrix Dashboard)功能,Hystrix 会持续地记录所有通过 Hystrix 发起的请求的执行信息,并以统计报表的形式展示给用户,包括每秒执行请求的数量、成功请求的数量和失败请求的数量等。
代码示例
pom 文件
<!--hystrix-dashboard 监控的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!--添加 Spring Boot 的监控模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置文件
# http://127.0.0.1:8204/hystrix-dashboard/hystrix 熔断器监控页面
# http://localhost:8203/hystrix/actuator/hystrix.stream 监控地址
hystrix:
dashboard:
proxy-stream-allow-list:
- "localhost"
启动类添加注解 @EnableHystrixDashboard
在开启熔断,被监控的模块下添加 config
@Configuration
public class HystrixDashboardConfig {
/**
* Hystrix dashboard 监控界面必须配置
* @return
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/actuator/hystrix.stream");//访问路径
registrationBean.setName("hystrix.stream");
return registrationBean;
}
}
服务限流
秒杀高并发等操作,严禁拥挤,排队有序进行 - 下一章 gateway 集成 sentinel