02_Sentinel组件使用
官方快速入门案例:
在官方文档中,定义的Sentinel进行资源保护的几个步骤:
1. 定义资源
2. 定义规则
3. 检验规则是否生效
Entry entry = null; // 务必保证 finally 会被执行 try { // 资源名可使用任意有业务语义的字符串 开启资源的保护 entry = SphU.entry("自定义资源名"); // 被保护的业务逻辑 method // do something... } catch (BlockException ex) { // 资源访问阻止,被限流或被降级 Sentinel定义异常 流控规则,降级规则,热点参数规则。。。。 服务降级(降级规则) // 进行相应的处理操作 } catch (Throwable ex) { // 若需要配置降级规则,需要通过这种方式记录业务异常 RuntimeException 服务降级 mock feign:fallback Tracer.traceEntry(ex, entry); } finally { // 务必保证 exit,务必保证每个 entry 与 exit 配对 if (entry != null) { entry.exit(); } }
Sentinel资源保护的方式:
基于API实现
1. 引入依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel‐core</artifactId> <version>1.8.4</version> </dependency>
2. 编写测试逻辑
private static final String RESOURCE_NAME = "HelloWorld"; @RequestMapping(value = "/hello") public String hello() { try (Entry entry = SphU.entry(RESOURCE_NAME)) { // 被保护的逻辑 log.info("hello world"); return "hello world"; } catch (BlockException ex) { // 处理被流控的逻辑 log.info("blocked!"); return "被流控了"; } } /** * 定义流控规则 */ @PostConstruct private static void initFlowRules() { List<FlowRule> rules = new ArrayList<>(); FlowRule rule = new FlowRule(); //设置受保护的资源 rule.setResource(RESOURCE_NAME); // 设置流控规则 QPS rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 设置受保护的资源阈值 rule.setCount(1); rules.add(rule); // 加载配置好的规则 FlowRuleManager.loadRules(rules); }
缺点:
业务侵入性很强,需要在controller中写入非业务代码。
配置不灵活,若需要添加新的受保护资源 需要手动添加 init方法来添加流控规则。
@SentinelResource注解实现
@SentinelResource 注解用来标识资源是否被限流、降级。
blockHandler: 定义当资源内部发生了BlockException应该进入的方法(捕获的是Sentinel定义的异常)
fallback: 定义的是资源内部发生了Throwable应该进入的方法
exceptionsToIgnore:配置fallback可以忽略的异常
源码入口:com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect
引入依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-annotation-aspectj</artifactId> <version>1.8.4</version> <scope>compile</scope> </dependency>
配置切面支持(非SpringBoot环境)
@Configuration public class SentinelAspectConfiguration { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } }
UserController中编写测试逻辑,添加@SentinelResource,并配置blockHandler和fallback
@SentinelResource(value = RESOURCE_NAME, blockHandler = "handleException", fallback = "fallbackException") @RequestMapping("/hello2") public String hello2() { int i = 1 / 0; return "helloworld"; } public String handleException(BlockException ex) { return "被流控了"; } public String fallbackException(Throwable t) { return "被异常降级了"; }
编写ExceptionUtil,注意如果指定了class,方法必须是static方法
public class ExceptionUtil { /** * 注意: 必须为 static 函数 */ public static R fallback(Integer id, Throwable e){ return R.error(-1,"===被异常降级啦==="); } public static R handleException(Integer id, BlockException e){ return R.error(-2,"===被限流啦==="); } }
@RequestMapping(value = "/findOrderByUserId/{id}") @SentinelResource(value = "findOrderByUserId", fallback = "fallback", fallbackClass = ExceptionUtil.class, blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class) public R findOrderByUserId(@PathVariable("id") Integer id) { //ribbon实现 String url = "http://mall‐order/order/findOrderByUserId/" + id; R result = restTemplate.getForObject(url, R.class); if (id == 4) { throw new IllegalArgumentException("非法参数异常"); } return result; }
Spring Cloud Alibaba整合Sentinel后会在springMVC的拦截器中声明为资源。
public abstract class AbstractSentinelInterceptor implements HandlerInterceptor:
流控规则设置可以通过Sentinel dashboard配置,客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信。
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>1.8.4</version> <scope>compile</scope> </dependency>
启动 Sentinel 控制台
下载控制台 jar 包并在本地启动:可以参见 此处文档
#启动控制台命令 java ‐jar sentinel‐dashboard‐1.8.4.jar
用户可以通过如下参数进行配置:
-Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 sentinel; -Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 123456; 如果省略这两个参数:默认用户和密码均为 sentinel; -Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示7200 秒;60m 表示 60 分钟,默认为 30 分钟;
访问http://localhost:8080/#/login ,默认用户名密码: sentinel/sentinel
Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包,所以要确保客户端有访问量;
Spring Cloud Alibaba整合Sentinel
引入依赖
<!--sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
添加yml配置,为微服务设置sentinel控制台地址
spring: application: name: sentinel-demo cloud: sentinel: transport: # 添加sentinel的控制台地址 dashboard: 127.0.0.1:8080 # 指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer port: 8719
资源名: 接口的API
针对来源: 默认是default,当多个微服务都调用这个资源时,可以配置微服务名来对指定的微服务设置阈值
阈值类型: 分为QPS和线程数 假设阈值为10
QPS类型: 只得是每秒访问接口的次数>10就进行限流
线程数: 为接受请求该资源分配的线程数>10就进行限流
测试: 因为QPS是1,所以1秒内多次访问会被限流。
访问http://localhost:8800/actuator/sentinel, 可以查看flowRules
微服务和Sentinel Dashboard通信原理
Sentinel控制台与微服务端之间,实现了一套服务发现机制,集成了Sentinel的微服务都会将元数据传递给Sentinel控制台,架构图如下所示:
测试:http://localhost:8719/getRules?type=flow 获取微服务流控规则信息
注意:随着微服务增加,端口是递增的,具体参考Sentinel Dashboard显示的微服务对应端口