基于 SpringCloud Alibaba Sentinel 实现网关动态限流
SpringCloud Alibaba Sentinel 的概念和来历
-
Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来保证微服务的稳定性
-
资源:可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其他应用提供的服务,甚至可以是一段代码
-
规则:围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则;且所有的规则可以动态的实时调整。
搭建 SpringCloud Alibaba Sentinel 控制台
-
获取并启动
Sentinel-DashBoard
-
启动命令 :
nohup java -Dserver.port=7777 -Dcsp.sentinel.dashboard.server=localhost:7777 -Dproject.name=guank-sentinel-dashboard -jar ../sentinel-dashboard-1.8.2.jar > sentinel.log &
-
从 Sentinel 1.6.0开始,Sentinel Dashboard 引入了基本的登录功能,默认的用户名密码都是sentinel
Sentinel 控制台的能力
- 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线
- 监控(单机和集群聚合): 通过 Sentinel 客户端暴露的监控API,定期拉去并且聚合应用监控信息,最终可以实现秒级的实时监控
- 规则管理和推送:统一管理推送规则
Sentinel 的功能及设计理念
-
流量控制
-
服务端需要根据系统的处理能力对流量进行控制,Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
-
-
Sentinel 流量控制方向
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系
- 运行指标,例如QPS、线程池、系统负载等
- 控制的效果,例如直接限流、冷启动、排队等
基于硬编码应用Sentinel
-
创建
e-commerce-sentinel-client
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>ecommerce-springcloud</artifactId> <groupId>com.imooc.ecommerce</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>e-commerce-sentinel-client</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <!-- 模块名及描述信息 --> <name>e-commerce-sentinel-client</name> <description>Sentinel Client</description> <dependencies> <!-- 创建工程需要的两个依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!-- Sentinel 适配了 Feign, 可以实现服务间调用的保护 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- Sentinel 使用 Nacos 存储规则 --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency> <!-- web 工程 --> <dependency> <groupId>com.imooc.ecommerce</groupId> <artifactId>e-commerce-mvc-config</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <!-- SpringBoot的Maven插件, 能够以Maven的方式为应用提供SpringBoot的支持,可以将 SpringBoot应用打包为可执行的jar或war文件, 然后以通常的方式运行SpringBoot应用 --> <build> <finalName>${artifactId}</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
-
编写
bootstrap.yml
server: port: 8100 servlet: context-path: /ecommerce-sentinel-client spring: application: name: e-commerce-sentinel-client # 应用名称也是构成 Nacos 配置管理 dataId 字段的一部分 (当 config.prefix 为空时) cloud: nacos: # 服务注册发现 discovery: enabled: true # 如果不想使用 Nacos 进行服务注册和发现, 设置为 false 即可 server-addr: 192.168.0.103:8848 # server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850 # Nacos 服务器地址 namespace: f2dfb4c7-a6cf-45d5-92e2-3912daf808d0 metadata: management: context-path: ${server.servlet.context-path}/actuator sentinel: # 配置 sentinel dashboard 地址 transport: dashboard: 192.168.0.103:7777 port: 8719 # 会在应用对应的机器上启动一个 Http Server, 该 Server 会与 Sentinel 控制台做交互 datasource: # 名称任意, 代表数据源 ds: nacos: # NacosDataSourceProperties.java 中定义 server-addr: ${spring.cloud.nacos.discovery.server-addr} dataId: ${spring.application.name}-sentinel namespace: ${spring.cloud.nacos.discovery.namespace} groupId: DEFAULT_GROUP data-type: json # 规则类型: com.alibaba.cloud.sentinel.datasource.RuleType # FlowRule 就是限流规则 rule-type: flow # 服务启动直接建立心跳连接 eager: true # 暴露端点 management: endpoints: web: exposure: include: '*' endpoint: health: show-details: always # 打开 Sentinel 对 Feign 的支持 feign: sentinel: enabled: true # 开启或关闭 @SentinelRestTemplate 注解 resttemplate: sentinel: enabled: trueserver: port: 8100 servlet: context-path: /ecommerce-sentinel-client spring: application: name: e-commerce-sentinel-client # 应用名称也是构成 Nacos 配置管理 dataId 字段的一部分 (当 config.prefix 为空时) cloud: nacos: # 服务注册发现 discovery: enabled: true # 如果不想使用 Nacos 进行服务注册和发现, 设置为 false 即可 server-addr: 192.168.0.103:8848 # server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850 # Nacos 服务器地址 namespace: f2dfb4c7-a6cf-45d5-92e2-3912daf808d0 metadata: management: context-path: ${server.servlet.context-path}/actuator sentinel: # 配置 sentinel dashboard 地址 transport: dashboard: 192.168.0.103:7777 port: 8719 # 会在应用对应的机器上启动一个 Http Server, 该 Server 会与 Sentinel 控制台做交互 datasource: # 名称任意, 代表数据源 ds: nacos: # NacosDataSourceProperties.java 中定义 server-addr: ${spring.cloud.nacos.discovery.server-addr} dataId: ${spring.application.name}-sentinel namespace: ${spring.cloud.nacos.discovery.namespace} groupId: DEFAULT_GROUP data-type: json # 规则类型: com.alibaba.cloud.sentinel.datasource.RuleType # FlowRule 就是限流规则 rule-type: flow # 服务启动直接建立心跳连接 eager: true # 暴露端点 management: endpoints: web: exposure: include: '*' endpoint: health: show-details: always # 打开 Sentinel 对 Feign 的支持 feign: sentinel: enabled: true # 开启或关闭 @SentinelRestTemplate 注解 resttemplate: sentinel: enabled: true
-
编码实例
/** * @author Guank * @version 1.0 * @description: 限流规则硬编码的 controller * @date 2022-07-17 15:09 */ @Slf4j @RestController @RequestMapping("/code") public class FlowRuleCodeController { /** * 初始化流控规则 */ @PostConstruct public void init() { //流控规则集合 List<FlowRule> flowRules = new ArrayList<>(); //创建流控规则 FlowRule flowRule = new FlowRule(); //设置流控规则 QPS ,限流阈值类型(QPS,并发线程数) flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); //流量控制手段 默认是直接抛弃 flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); //设置受保护的资源 flowRule.setResource("flowRuleCode"); //设置受保护的资源的阈值 QPS = 1 flowRule.setCount(1); flowRules.add(flowRule); //加载配置好的流控规则 FlowRuleManager.loadRules(flowRules); } /** * 使用硬编码的限流规则 * @return */ @GetMapping("/flow-rule") @SentinelResource(value = "flowRuleCode") public CommonResponse<String> flowRuleCode() { log.info("request flowRuleCode"); return new CommonResponse<>(0,"","imooc-guank-commerce"); } }
-
在
snetinel-dashboard
中配置规则新增流控规则
-
在资源被
Sentinel
流控后,处理的兜底方法/** * 当限流异常抛出时,指定调用的方法 兜底策略 * @param exception * @return */ public CommonResponse<String> handleException(BlockException exception) { log.error("has block exception : [{}]", JSON.toJSONString(exception.getRule()),exception); return new CommonResponse<>(-1, "flow rule exception", exception.getClass().getCanonicalName()); } --------------------------------------------------------------------------------------------- /** * 使用硬编码的限流规则 * @return */ @GetMapping("/flow-rule") // @SentinelResource(value = "flowRuleCode") @SentinelResource(value = "flowRuleCode",blockHandler = "handleException") public CommonResponse<String> flowRuleCode() { log.info("request flowRuleCode"); return new CommonResponse<>(0,"","imooc-guank-commerce"); }
注意:处理流控规则的兜底方法必须和流控限制的资源在同一个类里才会被扫描到,否则将不会被扫描到
返回值也需要和原来的方法一样
-
将兜底处理方法放到单独的类里
/** * @author Guank * @version 1.0 * @description: 自定义通用的限流处理逻辑 * @date 2022-07-17 16:12 */ @Slf4j public class GuankBlockHandler { /** * 通用限流处理方法 * 这个方法必须是 static 的 * @param exception * @return */ public static CommonResponse<String> guankHandlerBlockException(BlockException exception) { log.error("trigger guank block handler : [{}], [{}]", JSON.toJSONString(exception.getRule()),exception.getRuleLimitApp()); return new CommonResponse<>(-1, "flow rule trigger block exception", null); } }
-
在被限流的资源上编辑
/** * 使用硬编码的限流规则 * @return */ @GetMapping("/flow-rule") // @SentinelResource(value = "flowRuleCode") // @SentinelResource(value = "flowRuleCode",blockHandler = "handleException") @SentinelResource(value = "flowRuleCode", blockHandler = "guankHandlerBlockException", blockHandlerClass = {GuankBlockHandler.class}) public CommonResponse<String> flowRuleCode() { log.info("request flowRuleCode"); return new CommonResponse<>(0,"","imooc-guank-commerce"); }
评论区