摘要
本文将会讲述Spring cloud 整合sentinel 做限流和熔断的整合过程,本文的代码均为自己经过实际测试验证通过的,绝对不是什么网上一大抄抄来的垃圾文章。
本文将会讲述Spring cloud 整合sentinel 做限流和熔断的整合过程,本文的代码均为自己经过实际测试验证通过的,绝对不是什么网上一大抄抄来的垃圾文章。
凡是能看到这个页面的朋友,大概都是知道Spring Cloud gateway本身有自己原生的限流配置,但是由于Spring cloud Gateway中的那些限流配置不太适合生产实际使用,我才决定使用Sentinel来做限流和熔断。
Sentinel提供了限流、熔断、以及和数据持久化的配置,因此我认为Sentinel和Spring cloud 原生的限流和熔断机制(hystrix)相比,它简直是太强大了。
要使用Sentinel ,首先要了解的一个情况是我们必须使用它的可视化界面dashboard,为什么呢,因为我们要用这个页面做可视化的限流、熔断规则配置啊;其次,网上有绝大多数Spring Cloud 整合Sentinel的过程中都使用到了nacos,当时我很不解,为什么非要把nacos和Sentinel放在一起使用呢?现在我理解了,我给你们一个明确的答复:除非你们要用nacos做服务发现、配置中心,否则并非一定非要Sentinel和nacos一起使用(Spring cloud 的服务发现Eureka和Nacos不能同时使用,你看着办哦)。
至于为什么网上很多教程要把Sentinel和nacos一起使用,是因为他们要用nacos当做Sentinel的持久化配置的工具(呵呵),但实际上,Sentinel支持很多 持久化数据库,打脸要证明的哈,来一个图让你们看:
哦,我又看到了另一个引用的地方,这个地方说明的更详细,甚至连demo都准备好了https://github.com/alibaba/Sentinel/wiki/Dynamic-Rule-Configuration
至此,我可以说,如果你只是做限流和熔断,我们不用必须把Sentinel和Nacos联合起来一起使用(经历了迷茫和疑惑,我可以自豪的说,那些抄来的垃圾文章,你们滚蛋吧)。
从上一章我们可以分析到,配置的前提是 :
我们要安装Sentinel dashboard
我们整合Sentinel不必非要使用Nacos
我决定使用Redis作为Sentinel持久化的工具(分布式最好的选择)
首先我们要进行第一步,安装Sentinel dashboard,请访问最新的Sentinel dashboard 进行下载,我使用的V1.7.2。请下载sentinel-dashboard-x.y.x.jar,
下载到本地后,请使用如下命令运行这个jar(注意,你的机器必须安装了JDK,且请替换下面的jar的名字)
java -jar sentinel-dashboard-1.7.2.jar -Dserver.port=8719 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard
注意:
Dserver.port=8719指的是你的程序和Sentinel交互时使用的端口(http端口)
-Dcsp.sentinel.dashboard.server=localhost:8080指的是你在浏览器中访问的Sentinel dashboard的URL
启动成功后,通过浏览器访问以下链接,即可进入Sentinel dashboard登录页面
http://localhost:8080/
用户名和密码均为sentinel
登录成功后,进入dashboard首页(默认没有应用接入,空屏)
至此,Sentinel dashboard 安装完毕
向Gateway的pom中添加如下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
注意:
如果引用报错,请参考另一篇文章配置Spring cloud 整合 alibaba.cloud pom依赖报错的解决办法解决
以上依赖不保证会多引用了,但是保证能用(这一点确实是我偷懒了,因为我发现官方文档在这里也是东一锤子西一棒子,弄得我索性全引进来了)
spring: # 配置文件名称,用来标识不同环境的配置。由 spring.profiles.active 的值来决定使用哪组配置。 ## 简单尝试 profiles: route_test application: # 应用名称 name: bobfintech-gateway cloud: sentinel: transport: #dashboard地址 dashboard: localhost:8080 #对应自己的sentinel控制台端口 port: 8719 gateway: discovery: locator: # 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务 enabled: true # 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。) routes: # 路由标识(id:标识,具有唯一性) 简单尝试 - id: route_simple # 目标服务地址(uri:地址,请求转发后的地址) uri: lb://bobfintech-userservice # 路由条件(predicates:断言,匹配 HTTP 请求内容) predicates: ## 转发地址格式为 uri/archive - Path=/archive
注意,这里的关键是sentinel那些配置,必须有的哦,详细的参数配置可以看下官方原文:https://github.com/alibaba/Sentinel/wiki/Common-Configuration
package cn.com.xxxx.sentinel; import java.util.Collections; import java.util.List; import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.web.reactive.result.view.ViewResolver; import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter; import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler; /*** * @project: xxxx * @description: sentinel config * @version 1.0.0 * @errorcode * 错误码: 错误描述 * @author * <li>2020-07-15 guopengfei@xxxx.com.cn Create 1.0 * @copyright ©2019-2020 xxxx,版权所有。 * // */ @Configuration public class GatewayConfiguration { private final List<ViewResolver> viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) { this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { // Register the block exception handler for Spring Cloud Gateway. return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); } @Bean @Order(-1) public GlobalFilter sentinelGatewayFilter() { return new SentinelGatewayFilter(); } // @PostConstruct // public void doInit() { // initCustomizedApis(); // initGatewayRules(); // } // private void initCustomizedApis() { // Set<ApiDefinition> definitions = new HashSet<>(); // ApiDefinition api1 = new ApiDefinition("some_customized_api") // .setPredicateItems(new HashSet<ApiPredicateItem>() {{ // add(new ApiPathPredicateItem().setPattern("/ahas")); // add(new ApiPathPredicateItem().setPattern("/product/**") // .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); // }}); // ApiDefinition api2 = new ApiDefinition("another_customized_api") // .setPredicateItems(new HashSet<ApiPredicateItem>() {{ // add(new ApiPathPredicateItem().setPattern("/**") // .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); // }}); // definitions.add(api1); // definitions.add(api2); // GatewayApiDefinitionManager.loadApiDefinitions(definitions); // } // private void initGatewayRules() { // Set<GatewayFlowRule> rules = new HashSet<>(); // rules.add(new GatewayFlowRule("aliyun_route") // .setCount(10) // .setIntervalSec(1) // ); // rules.add(new GatewayFlowRule("aliyun_route") // .setCount(2) // .setIntervalSec(2) // .setBurst(2) // .setParamItem(new GatewayParamFlowItem() // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP) // ) // ); // rules.add(new GatewayFlowRule("httpbin_route") // .setCount(10) // .setIntervalSec(1) // .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER) // .setMaxQueueingTimeoutMs(600) // .setParamItem(new GatewayParamFlowItem() // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER) // .setFieldName("X-Sentinel-Flag") // ) // ); // rules.add(new GatewayFlowRule("httpbin_route") // .setCount(1) // .setIntervalSec(1) // .setParamItem(new GatewayParamFlowItem() // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM) // .setFieldName("pa") // ) // ); // rules.add(new GatewayFlowRule("httpbin_route") // .setCount(2) // .setIntervalSec(30) // .setParamItem(new GatewayParamFlowItem() // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM) // .setFieldName("type") // .setPattern("warn") // .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_CONTAINS) // ) // ); // // rules.add(new GatewayFlowRule("some_customized_api") // .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME) // .setCount(5) // .setIntervalSec(1) // .setParamItem(new GatewayParamFlowItem() // .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM) // .setFieldName("pn") // ) // ); // GatewayRuleManager.loadRules(rules); // } }
注意,这个配置类的作用是:
配置路由规则(被我注释掉了,因为我在application.yam中已经配置了,如果你想在程序中配置,请放开注释)
配置API组,这个组(不好意思啊,给忘了这个干啥了,自己翻去吧,哈哈哈嗝~)
这两个作用并非必须的,是否注释看你的需求,我本身没这个需求,所以注释掉了
不好意思,这个真没了(启动类不需要因为加入Sentinel而做任何的改变)
请运行你的Spring Cloud Gateway启动类,进行启动。启动成功后,请通过浏览器或者postman随便访问下你的gateway中的任意一个controller地址(很重要哦,要是不听话,你一会可能因为在Sentinel dashboard中看不到你的应用而骂娘,而且注意不要是Login、logout的那些东西)。
当你访问Controller成功后,前往Sentinel dashboard查看(刷新),是否已经看到了你的应用了呢,如果看到了,恭喜您,你可以在Sentinel dashboard配置你的限流和熔断了(这个下一篇将会讲解);如果仍旧没有看到,请检查application中配置的ip/端口等是否正确(还不行的话,您留言吧,我真没遇到这个问题)。
经过这篇文章,现在遗留的问题如下:
持久化Redis配置
如何配置限流和熔断
这将在下一篇文章中进行分析,请进入我的空间查看系列文章
各位亲朋好友,如果您认为这个文章不错,请多多转发,让更多的人走出迷雾,走向光明大道。