我是靠谱客的博主 暴躁犀牛,这篇文章主要介绍Spring cloud 整合Sentinel 实现zuul网关(gateway)限流redis缓存限流开始使用,现在分享给大家,希望可以做个参考。

关键词 Spring cloud Sentinel zuul gateway redis dashboard Sentinel redis 限流 

开始使用

修改的地方有两个,一个是Sentinel dashboard项目,一个是你自己的项目(客户端)

(不难,不难,不难,只要把文章看完就懂了,文章后面有修改dashboard的代码下载)

修改Sentinel dashboard项目

首先你需要down下来Sentinel整个项目源码,本文使用的为Sentinel 1.8.4的源码,源码下载路径为:官方Sentinel ,下载后请导入到开发工具(Eclipse 或者 idea)中准备修改源码并编译。

导入的项目结构如下:

我们将将要修改的代码就在sentinel-dashboard项目中。

此项目中要修改得地方有

  • application.properties(增加Redis配置参数)

  • pom.xml(增加Redis的依赖)

  • GatewayFlowRuleRedisProvider.java(新增,自定义的Redis拉取规则)

  • GatewayFlowRuleRedisPublisher.java(新增,自定义的Redis推送规则)

  • RedisConfig.java(新增,定义的Redis配置类)

  • RuleConstants.java(新增,Redis的常量)

  • GatewayApiController.java(修改,修改api推送逻辑和拉取逻辑)
  • GatewayFlowRuleController.java(修改,修改规则推送逻辑和拉取逻辑)

所有的修改内容已经罗列在上面了,下面咱们就详细说说如何修改:

application.properties文件

此文件在sentinel-dashboard项目下的resource中,在此文件最下面添加下面的配置:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# redis配置 spring.redis.database=0 spring.redis.host=localhost spring.redis.password=密码 spring.redis.port=6379 spring.redis.timeout=3000 #spring.redis.jedis.pool.max-active=8 #spring.redis.jedis.pool.max-wait=1 #spring.redis.jedis.pool.max-idle=8 #spring.redis.jedis.pool.min-idle=2 spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=1 spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=2

注意,上面的配置中,前四个配置为redis的配置,不在解释,自己修改成自己的就可以

pom.xml

此文件在sentinel-dashboard项目下,请添加以下依赖:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 集成redis --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-redis</artifactId> <version>1.8.4</version> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.5.12</version> </dependency> <!-- lettuce pool 缓存连接池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.2</version> </dependency>

注意,此处的${spring.boot.version}表示使用此项目本身配置的版本,以适应他的jar要求,防止jar依赖出问题。

GatewayApiRedisProvider.java

此文件新增,新增的位置com.alibaba.csp.sentinel.dashboard.rule.redis(注意,redis路径需要新增),如图:

上面所说的几个新增的类同样需要放在此路径下,这个类的作用是自定义基于Redis实现拉取限流规则的逻辑

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.alibaba.csp.sentinel.dashboard.rule.redis; import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * 自定义实现基于redis的拉取规则 * * @autheor lingguoxiong * @date 2022/10/9 23:08 */ @Component("gatewayApiRedisProvider") public class GatewayApiRedisProvider implements DynamicRuleProvider<List<ApiDefinitionEntity>> { @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public List<ApiDefinitionEntity> getRules(String appName) { System.out.println("Sentinel 从Redis拉取api begin >>>>>>>>>>>>>>>>>>>>"); String value = JSON.toJSONString(redisTemplate.opsForValue().get(RuleConstants.gatewayApi + appName)); if (StringUtils.isEmpty(value)) { return new ArrayList<>(); } System.out.println("Sentinel 从Redis拉取api end >>>>>>>>>>>>>>>>>>>>"); return JSONObject.parseArray(value, ApiDefinitionEntity.class); } }

GatewayApiRedisPublisher.java

此文件新增,新增的位置com.alibaba.csp.sentinel.dashboard.rule.redis,这个类的作用是自定义基于Redis实现推送限流规则的逻辑

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.alibaba.csp.sentinel.dashboard.rule.redis; import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SessionCallback; import org.springframework.stereotype.Component; import java.util.List; /** * 自定义实现限流配置推送规则 * * @autheor lingguoxiong * @date 2022/10/9 23:11 */ @Component("gatewayApiRedisPublisher") public class GatewayApiRedisPublisher implements DynamicRulePublisher<List<ApiDefinitionEntity>> { @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public void publish(String app, List<ApiDefinitionEntity> rules) { System.out.println("Sentinel 向Redis推送api begin >>>>>>>>>>>>>>>>>>>>" + rules); if (rules == null) { return; } System.out.println("rules:" + rules); // 使用RedisTemplate 的默认配置,即不开启事务支持。但是,我们可以通过使用 SessionCallback, // 该接口保证其内部所有操作都是在同一个Session中 List<Object> txResults = redisTemplate.execute(new SessionCallback<List<Object>>() { @Override public List<Object> execute(RedisOperations operations) throws DataAccessException { operations.multi(); operations.opsForValue().set(RuleConstants.gatewayApi + app, rules); operations.convertAndSend(RuleConstants.gatewayApiChannel + app, rules); // This will contain the results of all operations in the transaction return operations.exec(); } }); System.out.println("Sentinel 向Redis推送api end >>>>>>>>>>>>>>>>>>>>" + txResults); } }

RedisConfig.java

此文件新增,新增的位置com.alibaba.csp.sentinel.dashboard.rule.redis,这个类的作用是定义Redis的配置(这个类可用可不用)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.alibaba.csp.sentinel.dashboard.rule.redis; 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.core.StringRedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * @author lingguoxiong * @date 2021/12/20 18:08 */ @Configuration public class RedisConfig { @Bean(name = "redisTemplate") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(keySerializer()); redisTemplate.setHashKeySerializer(keySerializer()); redisTemplate.setValueSerializer(valueSerializer()); redisTemplate.setHashValueSerializer(valueSerializer()); return redisTemplate; } @Bean(name = "stringRedisTemplate") public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { StringRedisTemplate redisTemplate = new StringRedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(keySerializer()); redisTemplate.setHashKeySerializer(keySerializer()); redisTemplate.setValueSerializer(valueSerializer()); redisTemplate.setHashValueSerializer(valueSerializer()); return redisTemplate; } private RedisSerializer<String> keySerializer() { return new StringRedisSerializer(); } private RedisSerializer<Object> valueSerializer() { return new Jackson2JsonRedisSerializer(Object.class); } }

RuleConstants.java

此文件新增,新增的位置com.alibaba.csp.sentinel.dashboard.rule.redis,这个类的作用是定义Redis中使用的常量

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.alibaba.csp.sentinel.dashboard.rule.redis; import org.springframework.stereotype.Component; /** * @autheor lingguoxiong * @date 2022/10/9 23:19 */ @Component public class RuleConstants { /** * 流控规则key前缀 */ public static final String gatewayRuleFlow = "sentinel_gateway_rule_flow_"; public static final String gatewayRuleFlowChannel = "sentinel_gateway_rule_flow_channel_"; public static final String gatewayApi = "sentinel_gateway_api_"; public static final String gatewayApiChannel = "sentinel_gateway_api_channel_"; }

GatewayApiController.java

注意,这个类原本就存在,位于com.alibaba.csp.sentinel.dashboard.controller路径下,我们需要修改它的推送逻辑和拉取逻辑(此类基于sentinel 1.8.4修改,如果未来的您使用的版本比这个高,或者地,请谨慎修改---只动推送和拉取的逻辑)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.csp.sentinel.dashboard.controller.gateway; import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiPredicateItemEntity; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.AddApiReqVo; import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.ApiPredicateItemVo; import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.UpdateApiReqVo; import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemApiDefinitionStore; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; import com.alibaba.csp.sentinel.util.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.*; import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; /** * Gateway api Controller for manage gateway api definitions. * * @author cdfive * @since 1.7.0 */ @RestController @RequestMapping(value = "/gateway/api") public class GatewayApiController { private final Logger logger = LoggerFactory.getLogger(GatewayApiController.class); @Autowired private InMemApiDefinitionStore repository; @Autowired private SentinelApiClient sentinelApiClient; //-----------新增逻辑,服务于redis持久化--------------- @Autowired @Qualifier("gatewayApiRedisProvider") private DynamicRuleProvider<List<ApiDefinitionEntity>> ruleProvider; @Autowired @Qualifier("gatewayApiRedisPublisher") private DynamicRulePublisher<List<ApiDefinitionEntity>> rulePublisher; //-----------新增逻辑,服务于redis持久化--------------- @GetMapping("/list.json") @AuthAction(AuthService.PrivilegeType.READ_RULE) public Result<List<ApiDefinitionEntity>> queryApis(String app, String ip, Integer port) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } if (StringUtil.isEmpty(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } if (port == null) { return Result.ofFail(-1, "port can't be null"); } // 修改的地方 获取redis里面api的数据 try { List<ApiDefinitionEntity> apis = ruleProvider.getRules(app); repository.saveAll(apis); return Result.ofSuccess(apis); } catch (Throwable throwable) { logger.error("queryApis error:", throwable); return Result.ofThrowable(-1, throwable); } } @PostMapping("/new.json") @AuthAction(AuthService.PrivilegeType.WRITE_RULE) public Result<ApiDefinitionEntity> addApi(HttpServletRequest request, @RequestBody AddApiReqVo reqVo) { String app = reqVo.getApp(); if (StringUtil.isBlank(app)) { return Result.ofFail(-1, "app can't be null or empty"); } ApiDefinitionEntity entity = new ApiDefinitionEntity(); entity.setApp(app.trim()); String ip = reqVo.getIp(); if (StringUtil.isBlank(ip)) { return Result.ofFail(-1, "ip can't be null or empty"); } entity.setIp(ip.trim()); Integer port = reqVo.getPort(); if (port == null) { return Result.ofFail(-1, "port can't be null"); } entity.setPort(port); // API名称 String apiName = reqVo.getApiName(); if (StringUtil.isBlank(apiName)) { return Result.ofFail(-1, "apiName can't be null or empty"); } entity.setApiName(apiName.trim()); // 匹配规则列表 List<ApiPredicateItemVo> predicateItems = reqVo.getPredicateItems(); if (CollectionUtils.isEmpty(predicateItems)) { return Result.ofFail(-1, "predicateItems can't empty"); } List<ApiPredicateItemEntity> predicateItemEntities = new ArrayList<>(); for (ApiPredicateItemVo predicateItem : predicateItems) { ApiPredicateItemEntity predicateItemEntity = new ApiPredicateItemEntity(); // 匹配模式 Integer matchStrategy = predicateItem.getMatchStrategy(); if (!Arrays.asList(URL_MATCH_STRATEGY_EXACT, URL_MATCH_STRATEGY_PREFIX, URL_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy); } predicateItemEntity.setMatchStrategy(matchStrategy); // 匹配串 String pattern = predicateItem.getPattern(); if (StringUtil.isBlank(pattern)) { return Result.ofFail(-1, "pattern can't be null or empty"); } predicateItemEntity.setPattern(pattern); predicateItemEntities.add(predicateItemEntity); } entity.setPredicateItems(new LinkedHashSet<>(predicateItemEntities)); // 检查API名称不能重复 List<ApiDefinitionEntity> allApis = repository.findAllByMachine(MachineInfo.of(app.trim(), ip.trim(), port)); if (allApis.stream().map(o -> o.getApiName()).anyMatch(o -> o.equals(apiName.trim()))) { return Result.ofFail(-1, "apiName exists: " + apiName); } Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("add gateway api error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishApis(app, ip, port)) { logger.warn("publish gateway apis fail after add"); } return Result.ofSuccess(entity); } @PostMapping("/save.json") @AuthAction(AuthService.PrivilegeType.WRITE_RULE) public Result<ApiDefinitionEntity> updateApi(@RequestBody UpdateApiReqVo reqVo) { String app = reqVo.getApp(); if (StringUtil.isBlank(app)) { return Result.ofFail(-1, "app can't be null or empty"); } Long id = reqVo.getId(); if (id == null) { return Result.ofFail(-1, "id can't be null"); } ApiDefinitionEntity entity = repository.findById(id); if (entity == null) { return Result.ofFail(-1, "api does not exist, id=" + id); } // 匹配规则列表 List<ApiPredicateItemVo> predicateItems = reqVo.getPredicateItems(); if (CollectionUtils.isEmpty(predicateItems)) { return Result.ofFail(-1, "predicateItems can't empty"); } List<ApiPredicateItemEntity> predicateItemEntities = new ArrayList<>(); for (ApiPredicateItemVo predicateItem : predicateItems) { ApiPredicateItemEntity predicateItemEntity = new ApiPredicateItemEntity(); // 匹配模式 int matchStrategy = predicateItem.getMatchStrategy(); if (!Arrays.asList(URL_MATCH_STRATEGY_EXACT, URL_MATCH_STRATEGY_PREFIX, URL_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { return Result.ofFail(-1, "Invalid matchStrategy: " + matchStrategy); } predicateItemEntity.setMatchStrategy(matchStrategy); // 匹配串 String pattern = predicateItem.getPattern(); if (StringUtil.isBlank(pattern)) { return Result.ofFail(-1, "pattern can't be null or empty"); } predicateItemEntity.setPattern(pattern); predicateItemEntities.add(predicateItemEntity); } entity.setPredicateItems(new LinkedHashSet<>(predicateItemEntities)); Date date = new Date(); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { logger.error("update gateway api error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishApis(app, entity.getIp(), entity.getPort())) { logger.warn("publish gateway apis fail after update"); } return Result.ofSuccess(entity); } @PostMapping("/delete.json") @AuthAction(AuthService.PrivilegeType.DELETE_RULE) public Result<Long> deleteApi(Long id) { if (id == null) { return Result.ofFail(-1, "id can't be null"); } ApiDefinitionEntity oldEntity = repository.findById(id); if (oldEntity == null) { return Result.ofSuccess(null); } try { repository.delete(id); } catch (Throwable throwable) { logger.error("delete gateway api error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishApis(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { logger.warn("publish gateway apis fail after delete"); } return Result.ofSuccess(id); } private boolean publishApis(String app, String ip, Integer port) { //-----------新增逻辑,保存redis数据--------------- List<ApiDefinitionEntity> apis = repository.findAllByApp(app); try { //修改了redis中的存储及发布修改的规则,客户端app订阅channel后将规则保存在redis中 rulePublisher.publish(app, apis); // GatewayRuleManager logger.info("网关添加api成功{}"); } catch (Exception e) { e.printStackTrace(); logger.warn("publishRules failed Sentinel 推送api到Redis失败>>>>>>>>>>>>>>>>>>>>>>>>", e); return false; } //-----------新增逻辑,保存redis数据--------------- //核心代码,sentinel-dashboard通过http的形式进行数据推送,客户端接收后将规则保存在本地内存中 return sentinelApiClient.modifyApis(app, ip, port, apis); } }

在这个类中,我做了以下修改:

  • 引入了以下依赖

复制代码
1
2
3
4
5
6
7
8
//-----------新增逻辑,服务于redis持久化--------------- @Autowired @Qualifier("gatewayApiRedisProvider") private DynamicRuleProvider<List<ApiDefinitionEntity>> ruleProvider; @Autowired @Qualifier("gatewayApiRedisPublisher") private DynamicRulePublisher<List<ApiDefinitionEntity>> rulePublisher; //-----------新增逻辑,服务于redis持久化---------------

这两个就是上文中创建的Redis推送和拉取的类

  • 修改queryApis方法

  • 修改publishApis方法

这两出很重要,修改的时候需要重点关注,本类中其他的修改项不多,可以直接使用比对工具参考修改(如果您使用的是Sentinel 1.8.4,可以直接覆盖)。

至此,sentinel dashboard端的源码就修改完了(你可以执行mvn install 把这个源码编译成jar,然后运行方式和官方推荐方式相同,如果您不知道如何运行,请参考Spring cloud gateway 整合 sentinel 做限流和熔断(最新)解决。

客户端修改(你自己的项目)

这里说明下,网上很多资料写的乱七八糟,啥样的都有,更坑的是官方的文档写的也是很烂(要自己继承AbstractDataSource类,其实不用,因为最新的依赖中已经有一个实现类了,完全可以拿来用)

对于客户端,你需要做的是:

  • application.yml(增加sentinel和Redis配置)

  • pom.xml(增加redis和sentinel的依赖)

  • RedisDataSourceConfig(新增,应用启动增加Sentinel 和 redis 监听)

  • 启动的时候添加  -Dcsp.sentinel.app.type=1

application.yml

请在此文件新增您的redis和sentinel配置,示例如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spring: cloud: sentinel: transport: #dashboard地址 dashboard: localhost:8080 #对应自己的sentinel控制台端口 port: 8719 #您的项目和sentinel交互使用的端口 redis: database: 0 host: 127.0.0.1 port: 6379 password: #jedis: lettuce: pool: max-active: 8 max-idle: 8 min-wait: 0 timeout: 3000

配置这块不多说,redis客户端在于你自己的选择,sentinel配置请按照你自己的sentinel地址来配置

pom.xml

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> <version>2.2.8.RELEASE</version> </dependency> <!-- 控制台显示 类似 hystrix-dashboard --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>2.2.8.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-redis</artifactId> <version>1.8.4</version> </dependency>

ApiDefinitionDto.java类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.xdh.yjmthird.gateway.api; import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem; import java.util.Objects; import java.util.Set; /** * @autheor lingguoxiong * @date 2022/10/15 21:41 */ public class ApiDefinitionDto { private String apiName; private Set<ApiPathPredicateItem> predicateItems; public String getApiName() { return this.apiName; } public ApiDefinitionDto setApiName(String apiName) { this.apiName = apiName; return this; } public Set<ApiPathPredicateItem> getPredicateItems() { return this.predicateItems; } public ApiDefinitionDto setPredicateItems(Set<ApiPathPredicateItem> predicateItems) { this.predicateItems = predicateItems; return this; } @Override public boolean equals(Object o) { if (this == o) { return true; } else if (o != null && this.getClass() == o.getClass()) { ApiDefinitionDto that = (ApiDefinitionDto) o; return !Objects.equals(this.apiName, that.apiName) ? false : Objects.equals(this.predicateItems, that.predicateItems); } else { return false; } } @Override public int hashCode() { int result = this.apiName != null ? this.apiName.hashCode() : 0; result = 31 * result + (this.predicateItems != null ? this.predicateItems.hashCode() : 0); return result; } @Override public String toString() { return "ApiDefinition{apiName='" + this.apiName + ''' + ", predicateItems=" + this.predicateItems + '}'; } }

RedisDataSourceConfig.java

这里是唯一新增的类(类的位置你想放哪就放那,我不管,只要你老板不打你)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package com.xdh.yjmthird.gateway.config; import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager; import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.datasource.ReadableDataSource; import com.alibaba.csp.sentinel.datasource.redis.RedisDataSource; import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.xdh.yjmthird.gateway.api.ApiDefinitionDto; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; /** * @autheor lingguoxiong * @date 2022/10/18 22:25 */ @Slf4j @Component public class RedisDataSourceConfig implements ApplicationRunner { @Value("${spring.redis.host}") public String redisHost; @Value("${spring.redis.port}") public int redisPort; @Value("${spring.redis.password}") public String redisPass; public Integer database = 0; //限流规则key前缀 public final String GATEWAY_RULE_FLOW = "sentinel_gateway_rule_flow_gateway"; public final String GATEWAY_RULE_FLOW_CHANNEL = "sentinel_gateway_rule_flow_channel_gateway"; public final String GATEWAY_API = "sentinel_gateway_api_gateway"; public final String GATEWAY_API_CHANNEL = "sentinel_gateway_api_channel_gateway"; //降级规则key前缀 // public final String RULE_DEGRADE = "sentinel_rule_degrade_"; // public final String RULE_DEGRADE_CHANNEL = "sentinel_rule_degrade_channel"; //系统规则key前缀 // public final String RULE_SYSTEM = "sentinel_rule_system_"; // public final String RULE_SYSTEM_CHANNEL = "sentinel_rule_system_channel"; /** * ApplicationRunner * 该接口的方法会在服务启动之后被立即执行 * 主要用来做一些初始化的工作 * 但是该方法的运行是在SpringApplication.run(…) 执行完毕之前执行 */ @Override public void run(ApplicationArguments args) throws Exception { RedisConnectionConfig config = RedisConnectionConfig.builder().withHost(redisHost).withPort(redisPort) .withPassword(redisPass).withDatabase(database).build(); initApis(config); initRules(config); // Converter<String, List<DegradeRule>> parserDegrade = source -> JSON.parseObject(source, // new TypeReference<List<DegradeRule>>() { // }); // ReadableDataSource<String, List<DegradeRule>> redisDataSourceDegrade = new // RedisDataSource<>(config, RULE_DEGRADE + SentinelConfig.getAppName(), // RULE_DEGRADE_CHANNEL, parserDegrade); // DegradeRuleManager.register2Property(redisDataSourceDegrade.getProperty()); // Converter<String, List<SystemRule>> parserSystem = source -> JSON.parseObject(source, new // TypeReference<List<SystemRule>>() { // }); // ReadableDataSource<String, List<SystemRule>> redisDataSourceSystem = new // RedisDataSource<>(config, RULE_SYSTEM + SentinelConfig.getAppName(), RULE_SYSTEM_CHANNEL, // parserSystem); // SystemRuleManager.register2Property(redisDataSourceSystem.getProperty()); log.info(">>>>>>>>>执行sentinel规则初始化 end。。。"); } private void initApis(RedisConnectionConfig config) throws Exception { log.info("执行sentinel网关api初始化 start >>>>>>>>>>>>>"); Converter<String, Set<ApiDefinition>> parser = source -> JSON.parseObject(source, new TypeReference<Set<ApiDefinition>>() { }); ReadableDataSource<String, Set<ApiDefinition>> redisDataSource = new RedisDataSource<>(config, GATEWAY_API, GATEWAY_API_CHANNEL, parser); GatewayApiDefinitionManager.register2Property(redisDataSource.getProperty()); // 把redis数据重新拿出来转换 并设置(不然不会生效,里面是有数据的,不知道为什么不生效,可能是末尾资料文章说的没有LISTENER导致的,这个有时间大家自己研究下) Set<ApiDefinitionDto> apiDefinitionDtos = JSON.parseObject(redisDataSource.readSource(), new TypeReference<Set<ApiDefinitionDto>>() { }); if (apiDefinitionDtos != null) { Set<ApiDefinition> apiDefinitions = apiDefinitionDtos.stream().map(apiDefinitionDto -> { ApiDefinition apiDefinition = new ApiDefinition(); BeanUtils.copyProperties(apiDefinitionDto, apiDefinition); return apiDefinition; }).collect(Collectors.toSet()); GatewayApiDefinitionManager.loadApiDefinitions(apiDefinitions); } log.info("执行sentinel网关api初始化 end >>>>>>>>>>>>>"); } private void initRules(RedisConnectionConfig config) throws Exception { log.info("执行sentinel规则初始化 start >>>>>>>>>>>>>"); Converter<String, Set<GatewayFlowRule>> parser = source -> JSON.parseObject(source, new TypeReference<Set<GatewayFlowRule>>() { }); ReadableDataSource<String, Set<GatewayFlowRule>> redisDataSource = new RedisDataSource<>(config, GATEWAY_RULE_FLOW, GATEWAY_RULE_FLOW_CHANNEL, parser); GatewayRuleManager.register2Property(redisDataSource.getProperty()); // 把redis数据重新拿出来转换 并设置(不然不会生效,里面是有数据的,不知道为什么不生效,可能是末尾资料文章说的没有LISTENER导致的,这个有时间大家自己研究下) GatewayRuleManager.register2Property(redisDataSource.getProperty()); // 下面的这个需要去掉,附件源码里面的也需要去掉,之前其他文章说的不生效是上面 // ApiDefinition转换的问题 // Set<GatewayFlowRule> sets1 = GatewayRuleManager.getRules(); // Set<GatewayFlowRule> sets2 = new HashSet(); // GatewayRuleManager.loadRules(sets2); // GatewayRuleManager.loadRules(sets1); log.info("执行sentinel规则初始化 end >>>>>>>>>>>>>"); } }

这个类没有什么东西,最主要的代码就是初始化一个RedisConnectionConfig,并监听redis中的规则变化情况(注意,注释中的部分分别是熔断规则监听和数据规则监听,他们在sentinel dashboard中的修改本文没涉及,但是完全和限流规则使用方法完全一样,请自己实现),如果您有需要,可以放开此部分注释。

需要注意的是,这个类中引用的redis限流的KEY必须和sentinel dashboard中配置的一致,否则(呵呵,等自己骂娘吧)。

至此,客户端配置完成,分别启动自己的应用和编译好的sintinel dashboard项目,就能正常给你的项目添加限流配置,同时在redis中也能看到规则信息,并且重启您的项目后,sentinel dashboard也能加载出redis中缓存到规则(注意:此处会有一定的延迟,因为sentinel dashboard 需要定时任务去轮询redis中的规则,虽然不想放图毒害大家,但是还是要放上一两张证明我确实搞通了。

 

dashboard 修改的代码 (不需要积分)

https://download.csdn.net/download/Micheal_Bear/86780044

参考文章:

Sentinel的gateway规则持久化改造 - 灰信网(软件开发博客聚合)

sentinel实现gateway网关限流规则持久化 - 灰信网(软件开发博客聚合)

Spring cloud 整合Sentinel 实现redis缓存限流规则(最新一)|Redis 持久化 SentinelCDCN-码上中国博客

Spring cloud 整合Sentinel 实现redis缓存限流规则(最新二)|sentinel 持久化redisCDCN-码上中国博客

最后

以上就是暴躁犀牛最近收集整理的关于Spring cloud 整合Sentinel 实现zuul网关(gateway)限流redis缓存限流开始使用的全部内容,更多相关Spring内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(136)

评论列表共有 0 条评论

立即
投稿
返回
顶部