摘要
Spring cloud gateway 整合 sentinel 做限流和熔断,同时将规则缓存到Redis中最新教程,本文为作者结合最新的Sentinel v1.7.2整合的Redis持久化Sentinel限流规则、用redis持久化Sentinel规则的教程。
Spring cloud gateway 整合 sentinel 做限流和熔断,同时将规则缓存到Redis中最新教程,本文为作者结合最新的Sentinel v1.7.2整合的Redis持久化Sentinel限流规则、用redis持久化Sentinel规则的教程。
不同于网上的其他文章,抄来抄去都是一样的,本文是严格参照官方最新文档,代码都是经过自己实际测试,并非抄袭网上的现有文章来做的,如果您觉得文章对您有帮助,还请多多转发,让更多的朋友能够借助本文少走弯路。
大致先普及一下Senttinel的服务端和客户端规则交互的方式,已经熟悉的请跳过此前言部分。
我们知道Sentinel具有监控,限流、熔断规则管理和推送(拉模式不在本篇文章的探讨范围之内)的功能,而规则的推送,Sentinel有三种方式:原始模式、pull模式、push模式。
这三种模式的比较如下(详见官方文档):
推送模式 | 说明 | 有点 | 缺点 |
原始模式 | API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource) | 简单,无任何依赖 | 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境 |
Pull 模式 | 扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件等 | 简单,无任何依赖;规则持久化 | 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。 |
Push 模式 | 扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 | 规则持久化;一致性;快速 | 引入第三方依赖 |
参考上述对比,我们可以知道,如果您要把Sentinel部署至生产环境,官方是推荐使用Push模式的,当然这种方式使用起来是有一定的成本的。
在我的上一篇文章中,我们也说了Sentinel支持多种持久化工具,包含静态文件数据源、ZooKeeper 数据源、Redis 数据源、Nacos 数据源、Apollo 数据源、Spring Cloud Config 数据源、Etcd 数据源、Consul 数据源这几种,我们本篇文章将会讲解如何使用Redis数据源来持久化Sentinel规则。
需要注意的是,Sentinel官方明确说明:Sentinel结合这些数据源做持久化一般是需要修改Sentinel dashboard源码的,本文就是需要在修改Sentinel dashboard源码的基础上,增加了适合Redis持久化Sentinel限流规则和熔断规则的逻辑。需要会修改flowRuleRedisProvider和flowRuleRedisPublisher,以及一些其他的文件,具体修改的那些地方,在下文会有详细的说明。
修改的地方有两个,一个是Sentinel dashboard项目,一个是你自己的项目(客户端)
首先你需要down下来Sentinel整个项目源码,本文使用的为Sentinel 1.7.2的源码,源码下载路径为:官方Sentinel ,下载后请导入到开发工具(Eclipse 或者 idea)中准备修改源码并编译。
导入的项目结构如下:
我们将将要修改的代码就在sentinel-dashboard项目中。
此项目中要修改得地方有
application.properties(增加Redis配置参数)
pom.xml(增加Redis的依赖)
FlowRuleRedisProvider.java(新增,自定义的Redis拉取规则)
FlowRuleRedisPublisher.java(新增,自定义的Redis推送规则)
RedisConfig.java(新增,定义的Redis配置类)
RuleConstants.java(新增,Redis的常量)
FlowControllerV1.java(修改,修改规则推送逻辑和拉取逻辑)
所有的修改内容已经罗列在上面了,下面咱们就详细说说如何修改:
此文件在sentinel-dashboard项目下的resource中,在此文件最下面添加下面的配置:
# Redis\u6570\u636E\u5E93\u7D22\u5F15\uFF08\u9ED8\u8BA4\u4E3A0\uFF09 spring.redis.database=0 # Redis\u670D\u52A1\u5668\u5730\u5740 spring.redis.host=127.0.0.1 # Redis\u670D\u52A1\u5668\u8FDE\u63A5\u7AEF\u53E3 spring.redis.port=6379 # \u8FDE\u63A5\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09 spring.redis.timeout=30000 #\u6D41\u63A7\u89C4\u5219key\u524D\u7F00 rule.flow=sentinel_rule_flow_
注意,上面的配置中,前四个配置为redis的配置,不在解释,自己修改成自己的就可以,“rule.flow=sentinel_rule_flow_”为你要存到Redis中的KEY,这个配置在RuleConstants.java被引用了
此文件在sentinel-dashboard项目下,请添加以下依赖:
<!-- 集成redis --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-redis</artifactId> <version>1.7.1</version> </dependency> <!--redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>${spring.boot.version}</version> </dependency>
注意,此处的${spring.boot.version}表示使用此项目本身配置的版本,以适应他的jar要求,防止jar依赖出问题。
此文件新增,新增的位置com.alibaba.csp.sentinel.dashboard.rule.redis(注意,redis路径需要新增),如图:
上面所说的几个新增的类同样需要放在此路径下,这个类的作用是自定义基于Redis实现拉取限流规则的逻辑
package com.alibaba.csp.sentinel.dashboard.rule.redis; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.fastjson.JSONObject; /** * @project: xxx * @description: 自定义实现基于redis的拉取规则 * @version 1.0.0 * @errorcode * 错误码: 错误描述 * @author * <li>2020-07-17 guopengfei@bobfintech.com.cn Create 1.0 * @copyright ©2019-2020 xxxx,版权所有。 */ @Component("flowRuleRedisProvider") public class FlowRuleRedisProvider implements DynamicRuleProvider<List<FlowRuleEntity>> { @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private RuleConstants ruleConstants; @Override public List<FlowRuleEntity> getRules(String appName) throws Exception { System.out.println("Sentinel 从Redis拉取规则 begin >>>>>>>>>>>>>>>>>>>>"); Object value1 = redisTemplate.opsForValue().get(ruleConstants.ruleFlow + appName); String value = (String) redisTemplate.opsForValue().get(ruleConstants.ruleFlow + appName); if (StringUtils.isEmpty(value)){ return new ArrayList<>(); } System.out.println("Sentinel 从Redis拉取规则 end >>>>>>>>>>>>>>>>>>>>"); return JSONObject.parseArray(value,FlowRuleEntity.class); } }
此文件新增,新增的位置com.alibaba.csp.sentinel.dashboard.rule.redis,这个类的作用是自定义基于Redis实现推送限流规则的逻辑
package com.alibaba.csp.sentinel.dashboard.rule.redis; import java.util.List; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.annotation.JsonAlias; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; /** * @project: xxxx * @description: 自定义实现限流配置推送规则 * @version 1.0.0 * @errorcode * 错误码: 错误描述 * @author * <li>2020-07-17 guopengfei@bobfintech.com.cn Create 1.0 * @copyright ©2019-2020 xxxx,版权所有。 */ @Component("flowRuleRedisPublisher") public class FlowRuleRedisPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> { @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private RuleConstants ruleConstants; @Override public void publish(String app, List<FlowRuleEntity> rules) throws Exception { System.out.println("Sentinel 向Redis推送规则 begin >>>>>>>>>>>>>>>>>>>>"); if (rules == null){ return; } redisTemplate.multi(); redisTemplate.opsForValue().set(ruleConstants.ruleFlow+app, JSONObject.toJSONString(rules)); redisTemplate.convertAndSend(app,rules); redisTemplate.exec(); System.out.println("Sentinel 向Redis推送规则 end >>>>>>>>>>>>>>>>>>>>"); } }
此文件新增,新增的位置com.alibaba.csp.sentinel.dashboard.rule.redis,这个类的作用是定义Redis的配置(这个类可用可不用)
package com.alibaba.csp.sentinel.dashboard.rule.redis; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; /** * @project: xxxx * @description: Redis配置 * @version 1.0.0 * @errorcode * 错误码: 错误描述 * @author * <li>2020-07-17 guopengfei@bobfintech.com.cn Create 1.0 * @copyright ©2019-2020 xxxx,版权所有。 */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setConnectionFactory(factory); // 打开事务 redisTemplate.setEnableTransactionSupport(true); // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); serializer.setObjectMapper(mapper); redisTemplate.setValueSerializer(serializer); // 使用StringRedisSerializer来序列化和反序列化redis的key值 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(serializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
此文件新增,新增的位置com.alibaba.csp.sentinel.dashboard.rule.redis,这个类的作用是定义Redis中使用的常量,里面只定义了一个,就是上文说的Redis保存规则的KEY
package com.alibaba.csp.sentinel.dashboard.rule.redis; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * @project: xxxx * @description: Redis常量 * @version 1.0.0 * @errorcode * 错误码: 错误描述 * @author * <li>2020-07-17 guopengfei@bobfintech.com.cn Create 1.0 * @copyright ©2019-2020 xxxx,版权所有。 */ @Component public class RuleConstants { /** * 流控规则key前缀 */ @Value("${rule.flow}") public String ruleFlow; }
注意,这个类原本就存在,位于com.alibaba.csp.sentinel.dashboard.controller路径下,我们需要修改它的推送逻辑和拉取逻辑(此类基于sentinel 1.7.2修改,如果未来的您使用的版本比这个高,或者地,请谨慎修改---只动推送和拉取的逻辑)
Srping cloud 整合Sentinel 实现redis缓存限流规则(最新二)