隔离和降级
虽然限流可以尽量避免因高并发引起的服务·故障,但服务还会因为其他原因而故障。如果要将这些故障控制在一定的范围内,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级来实现了。
不管是线程隔离还是熔断降级,都是对**客户端(调用方)**的保护
由于微服务中的服务远程调用,是通过Feign来实现的,由此引出Feign整合Sentinel
Feign整合Sentinel
相关依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1.配置文件application.yml添加配置,开启feign的sentinel功能
feign:
httpclient:
enabled: true # 支持HttpClient的开关
max-connections: 200 # 最大连接数
max-connections-per-route: 50 # 单个路径的最大连接数
sentinel:
enabled: true # 开启feign对于sentinel的支持
2.给FeignClient的客户端,编写请求失败后的降级逻辑
FallbackClass,无法对远程调用的异常做处理
FallbackFactory,可以对远程调用的异常做处理,选择这种
实现FallbackFactory接口
@Slf4j
//实现FallbackFactory 接口,指定关联的客户端接口
public class UserClientFallBackFactory implements FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable throwable) {
return new UserClient() {
@Override
public User findById(Long id) {
log.error("查询用户异常",throwable);
return new User();
}
};
}
}
配置类中注入bean
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel(){
return Logger.Level.BASIC;
}
//bean的注册
@Bean
public UserClientFallBackFactory userClientFallBackFactory(){
return new UserClientFallBackFactory();
}
}
@FeignClient注解中指定实现类
// 注解中指定
@FeignClient(value = "userservice",fallbackFactory = UserClientFallBackFactory.class)
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
重启服务,调用后,查看相关链路
线程隔离
线程隔离的两种方式实现:
线程池隔离与信号量隔离(Sentinel默认采用)
线程池隔离
优点:支持主动超时、支持异步调用
缺点:线程的额外开销比较大
场景:低扇出
什么是扇出?一个服务依赖于N个服务,N越多,扇出越高
信号量隔离
优点:轻量级、无额外的开销
缺点:不支持主动超时、不支持异步调用
场景:高频调用、高扇出
线程隔离(舱壁模式)
在添加限流规则时,选择线程数的阈值类型
线程数:该资源能够使用的tomcat线程数的最大值,也就是通过限制线程数量,实现舱壁模式。
功能演示
给 UserClient的查询用户接口设置流控规则,线程数不能超过 2。然后利用jemeter测试
控制台输出:
jemeter中没有异常,因为会走前面返回空用户的降级逻辑,所以没有抛出相关的异常
熔断降级
熔断降级时解决雪崩问题的重要手段,其思路时由**断路器**统计服务调用的异常比例,慢请求的比例,如果超出阈值则会**熔断**该服务。即拦截访问该服务的一切请求,而**当服务恢复时,断路器会放行访问该服务的请求**。
**实现原理:状态机**
熔断策略-慢调用
断路器熔断策略有三种:慢调用、异常比例、异常数
慢调用:业务的响应时长(RT) 大于指定时长的请求认定为慢调用请求。在指定的时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。
RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
演示
给 UserClient的查询用户接口设置降级规则**,慢调用的RT阈值为50ms**,统计时间为1秒,最小请求数量为5,失败阈值比例为0.4,熔断时长为5
增加业务耗时
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id,
@RequestHeader(value = "Truth", required = false) String truth)
throws InterruptedException {
System.out.println("truth: " + truth);
if(id==1){
//id=1 触发慢调用
Thread.sleep(60);
}
return userService.queryById(id);
}
}
降级规则
熔断后,去查询order为102的服务,查询用户返回空
超过熔断时长的时间范围后,又恢复
异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或者超过指定异常数),则触发熔断。
统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于0.4,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
按照异常数的次数,设置熔断规则
演示
给 UserClient的查询用户接口设置降级规则,统计时间为1秒,最小请求数量为5,失败阈值比例为0.4,熔断时长为5s
模拟异常
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id,
@RequestHeader(value = "Truth", required = false) String truth)
throws InterruptedException {
System.out.println("truth: " + truth);
if(id==1){
//id=1 触发慢调用
Thread.sleep(60);
}
if(id==2){
throw new RuntimeException("熔断策略--异常比例");
}
return userService.queryById(id);
}
配置规则
访问order 103 触发熔断,返回空User