Sentinel 隔离和降级
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情
如果有需要可以查看官方文档sentinelguard.io/zh-cn/docs/…
流量控制是一种预防措施,能够减缓雪崩发生的情况,但是无法完全隔绝。如果想要将雪崩问题控制在一定的范围,需要通过线程隔离和熔断降级这两个手段了。
-
**线程隔离:**消费者远程调用提供者方法的时候,为每个远程调用的请求分配独立线程池。当某一个提供者出现故障时,最多消耗该提供者对应请求的独立线程池资源,避免消费者的所有资源被耗尽。
-
**熔断降级:**在消费者远程调用提供者的过程中,加入一个断路器。断路器统计提供者响应失败的次数,若 失败次数/总请求次数 比例过高,则熔断该业务,不允许再访问该服务的提供者。
无论是线程隔离或者熔断降级都是对提供者的限制和监管,对消费者的保护。在提供者出现故障的时候对消费者通过线程隔离、熔断降级进行保护。
我们的微服务远程调用通过 Feign 完成,因此我们将 Feign 和 Sentinel 进行整合,在 Feign 中实现线程隔离和熔断降级。
1. FeignClient 整合 Sentinel
- Feign 整合 Sentinel 步骤:
- 在application.yml中配置:feign.sentienl.enable=true
- 给FeignClient编写FallbackFactory并注册为Bean
- 将FallbackFactory配置到FeignClient
1.1 修改配置,开启 Sentinel 功能
-
修改消费者的 application.yml 文件,开启 Feign 的 Sentinel 功能:
feign: sentinel: enabled: true # 开启 feign 对 sentinel 的支持
1.2 编写失败降级逻辑
当提供者出现故障的时候,对应的请求返回的是异常信息。我们不能让异常信息赤裸裸的展示在用户面前,通常会给用户返回一个友好提示或默认结果。这个就是请求失败后的降级逻辑,也叫做失败降级逻辑。
-
给 Feign 编写失败后的降级逻辑,通常有两种方法:
- FallbackClass,无法对远程调用做异常处理
- FallbackFactory,可以对远程调用的异常做处理
我们通常选择第二种方法做失败降级处理
接下来我们将演示第二种方法 FallbackFactory 处理方法
-
在 Feign 里定义 FallbackFactory
代码:
package cn.itcast.feign.clients.fallback; import cn.itcast.feign.clients.UserClient; import cn.itcast.feign.pojo.User; import feign.hystrix.FallbackFactory; import lombok.extern.slf4j.Slf4j; /** * @author HGD * @date 2022/8/17 12:09 */ @Slf4j 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(); } }; } } -
将上面的 FallbackFactory 注册成 Bean
package cn.itcast.feign.config; import cn.itcast.feign.clients.fallback.UserClientFallbackFactory; import feign.Logger; import org.springframework.context.annotation.Bean; public class DefaultFeignConfiguration { @Bean public Logger.Level logLevel(){ return Logger.Level.BASIC; } /** * 将 FallbackFactory 注册到 Bean 中 * @return new UserClientFallbackFactory(); */ @Bean public UserClientFallbackFactory userClientFallbackFactory() { return new UserClientFallbackFactory(); } } -
在 Feign 中提供者的接口中添加
@FeignClient(fallbackFactory = FallbackFactory.class)注解代码:
package cn.itcast.feign.clients; import cn.itcast.feign.clients.fallback.UserClientFallbackFactory; import cn.itcast.feign.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class) public interface UserClient { @GetMapping("/user/{id}") User findById(@PathVariable("id") Long id); }
完成所有步骤之后重启消费者的服务,访问一下涉及远程调用的业务,然后查看 Sentinel 控制台
2. 线程隔离(舱壁模式)
2.1 线程隔离的实现方式
线程隔离通过两种方式实现:
-
线程池隔离
为每个远程调用的请求分配独立线程池,利用线程池本身实现隔离效果
-
信号量隔离
不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求
方式二是 Sentinel 默认方法
-
两者优缺点
优点 缺点 场景 信号量隔离 轻量级,无额外开销 不支持主动超时,不支持异步调用 高频使用,高输出 线程池隔离 支持主动超时,支持异步调用 线程的额外开销比较大 低扇出,调用较少的情况 低扇出:一个类,尽可能不要去依赖别的类,就是所谓的low fan out
主动超时:能主动控制方法执行的超时时间,如果超时了或有异常就抛出异常
2.2 Sentinel 的线程隔离
-
配置规则
-
案例
-
配置隔离规则
选择 Feign 接口后面的流控按钮:
填写表单:
-
使用 JMeter 测试
选择《阈值类型-线程数<2》:
一次发生10个请求,有较大概率并发线程数超过2,而超出的请求会走之前定义的失败降级逻辑。
查看运行结果:
发现虽然结果都是通过了,不过部分请求得到的响应是降级返回的null信息。
-
2.3.总结
线程隔离的两种手段是?
-
信号量隔离
-
线程池隔离
信号量隔离的特点是?
- 基于计数器模式,简单,开销小
线程池隔离的特点是?
- 基于线程池模式,有额外开销,但隔离控制更强