Skip to content

Commit bb30df3

Browse files
authored
Merge pull request dromara#502 from KamToHung/fix-501
Redis限流支持cluster模式
2 parents 8ae462b + 212ff6a commit bb30df3

File tree

9 files changed

+165
-44
lines changed

9 files changed

+165
-44
lines changed

extension/extension-limiter-redis/src/main/java/org/dromara/dynamictp/extension/limiter/redis/ratelimiter/AbstractRedisRateLimiter.java

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.springframework.data.redis.core.script.RedisScript;
2424
import org.springframework.scripting.support.ResourceScriptSource;
2525

26-
import java.time.Instant;
2726
import java.util.Collections;
2827
import java.util.List;
2928
import java.util.Objects;
@@ -61,22 +60,12 @@ public RedisScript<List<Long>> getScript() {
6160
return script;
6261
}
6362

64-
@Override
65-
public List<String> getKeys(final String key) {
66-
String cacheKey = PREFIX + ":" + key;
67-
return Collections.singletonList(cacheKey);
68-
}
69-
70-
@Override
71-
public List<Long> isAllowed(String key, long windowSize, int limit) {
63+
public List<Object> isAllowed(String key, long windowSize, int limit) {
7264
RedisScript<?> script = this.getScript();
7365
List<String> keys = this.getKeys(key);
74-
66+
String[] values = this.getArgs(key, windowSize, limit);
7567
return Collections.unmodifiableList((List) Objects.requireNonNull(stringRedisTemplate.execute(script, keys,
76-
doubleToString(windowSize), doubleToString(limit), doubleToString(Instant.now().getEpochSecond()))));
68+
values)));
7769
}
7870

79-
private String doubleToString(final double param) {
80-
return String.valueOf(param);
81-
}
8271
}

extension/extension-limiter-redis/src/main/java/org/dromara/dynamictp/extension/limiter/redis/ratelimiter/NotifyRedisRateLimiterFilter.java

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
package org.dromara.dynamictp.extension.limiter.redis.ratelimiter;
1919

2020
import lombok.extern.slf4j.Slf4j;
21-
import lombok.val;
22-
import org.apache.commons.collections4.CollectionUtils;
2321
import org.dromara.dynamictp.common.pattern.filter.Invoker;
2422
import org.dromara.dynamictp.core.notifier.chain.filter.NotifyFilter;
2523
import org.dromara.dynamictp.core.notifier.context.BaseNotifyCtx;
@@ -36,8 +34,6 @@
3634
@Slf4j
3735
public class NotifyRedisRateLimiterFilter implements NotifyFilter {
3836

39-
public static final int LUA_RES_REMAIN_INDEX = 2;
40-
4137
@Resource
4238
private RedisRateLimiter<List<Long>> redisScriptRateLimiter;
4339

@@ -49,27 +45,10 @@ public int getOrder() {
4945
@Override
5046
public void doFilter(BaseNotifyCtx context, Invoker<BaseNotifyCtx> nextFilter) {
5147
String notifyName = context.getExecutorWrapper().getThreadPoolName() + ":" + context.getNotifyItemEnum().getValue();
52-
boolean checkResult = check(notifyName, context.getNotifyItem().getClusterLimit(),
53-
context.getNotifyItem().getInterval());
48+
boolean checkResult = redisScriptRateLimiter.check(notifyName, context.getNotifyItem().getInterval(), context.getNotifyItem().getClusterLimit());
5449
if (checkResult) {
5550
nextFilter.invoke(context);
5651
}
5752
}
5853

59-
private boolean check(String notifyName, int limit, long interval) {
60-
try {
61-
val res = redisScriptRateLimiter.isAllowed(notifyName, interval, limit);
62-
if (CollectionUtils.isEmpty(res)) {
63-
return true;
64-
}
65-
if (res.get(LUA_RES_REMAIN_INDEX) <= 0) {
66-
log.debug("DynamicTp notify, trigger redis rate limit, limitKey:{}", res.get(0));
67-
return false;
68-
}
69-
return true;
70-
} catch (Exception e) {
71-
log.error("DynamicTp notify, redis rate limit check failed, limitKey:{}", notifyName, e);
72-
return true;
73-
}
74-
}
7554
}

extension/extension-limiter-redis/src/main/java/org/dromara/dynamictp/extension/limiter/redis/ratelimiter/RedisRateLimiter.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,23 @@ public interface RedisRateLimiter<T> {
4646
List<String> getKeys(String key);
4747

4848
/**
49-
* If allowed.
49+
* Get args.
5050
*
5151
* @param key the key
5252
* @param windowSize the window size
5353
* @param limit the limit
54+
* @return the args
55+
*/
56+
String[] getArgs(String key, long windowSize, int limit);
57+
58+
/**
59+
* check.
60+
*
61+
* @param name the key
62+
* @param interval the interval
63+
* @param limit the limit
5464
* @return the result
5565
*/
56-
T isAllowed(String key, long windowSize, int limit);
66+
boolean check(String name, long interval, int limit);
67+
5768
}

extension/extension-limiter-redis/src/main/java/org/dromara/dynamictp/extension/limiter/redis/ratelimiter/SlidingWindowRateLimiter.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,64 @@
1717

1818
package org.dromara.dynamictp.extension.limiter.redis.ratelimiter;
1919

20+
import lombok.extern.slf4j.Slf4j;
21+
import lombok.val;
22+
import org.apache.commons.collections4.CollectionUtils;
2023
import org.dromara.dynamictp.common.util.CommonUtil;
2124
import org.dromara.dynamictp.extension.limiter.redis.em.RateLimitEnum;
2225
import org.springframework.data.redis.core.StringRedisTemplate;
2326

24-
import java.util.Arrays;
27+
import java.time.Instant;
28+
import java.util.Collections;
2529
import java.util.List;
30+
import java.util.Objects;
2631

2732
/**
2833
* SlidingWindowRateLimiter related
2934
*
3035
* @author yanhom
3136
* @since 1.0.8
3237
**/
38+
@Slf4j
3339
public class SlidingWindowRateLimiter extends AbstractRedisRateLimiter {
3440

41+
public static final int LUA_RES_REMAIN_INDEX = 2;
42+
3543
public SlidingWindowRateLimiter(StringRedisTemplate stringRedisTemplate) {
3644
super(RateLimitEnum.SLIDING_WINDOW.getScriptName(), stringRedisTemplate);
3745
}
3846

3947
@Override
4048
public List<String> getKeys(final String key) {
4149
String cacheKey = CommonUtil.getInstance().getServiceName() + ":" + PREFIX + ":" + key;
50+
return Collections.singletonList(cacheKey);
51+
}
52+
53+
@Override
54+
public String[] getArgs(String key, long windowSize, int limit) {
4255
String memberKey = CommonUtil.getInstance().getIp() + ":" + COUNTER.incrementAndGet();
43-
return Arrays.asList(cacheKey, memberKey);
56+
return new String[]{doubleToString(windowSize), doubleToString(limit), doubleToString(Instant.now().getEpochSecond()), memberKey};
4457
}
58+
59+
public boolean check(String name, long interval, int limit) {
60+
try {
61+
val res = isAllowed(name, interval, limit);
62+
if (CollectionUtils.isEmpty(res)) {
63+
return true;
64+
}
65+
if (Objects.isNull(res.get(LUA_RES_REMAIN_INDEX)) || (long) res.get(LUA_RES_REMAIN_INDEX) <= 0) {
66+
log.debug("DynamicTp notify, trigger redis rate limit, limitKey:{}", res.get(0));
67+
return false;
68+
}
69+
return true;
70+
} catch (Exception e) {
71+
log.error("DynamicTp notify, redis rate limit check failed, limitKey:{}", name, e);
72+
return true;
73+
}
74+
}
75+
76+
private String doubleToString(final double param) {
77+
return String.valueOf(param);
78+
}
79+
4580
}

extension/extension-limiter-redis/src/main/resources/scripts/sliding_window_rate_limiter.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
local key = KEYS[1]
2-
local member = KEYS[2]
32

43
local window_size = tonumber(ARGV[1])
54
local limit = tonumber(ARGV[2])
65
local timestamp = tonumber(ARGV[3])
6+
local member = ARGV[4]
77

88
local accepted = 0
99
local exists_key = redis.call('exists', key)

test/test-extension/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
<modelVersion>4.0.0</modelVersion>
44
<parent>
55
<groupId>org.dromara.dynamictp</groupId>
6-
<artifactId>dynamic-tp-all</artifactId>
6+
<artifactId>dynamic-tp-test</artifactId>
77
<version>${revision}</version>
8-
<relativePath>../../pom.xml</relativePath>
8+
<relativePath>../pom.xml</relativePath>
99
</parent>
1010

1111
<artifactId>dynamic-tp-test-extension</artifactId>
12-
<packaging>jar</packaging>
12+
<packaging>pom</packaging>
1313

1414
<name>test-extension</name>
1515

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>org.dromara.dynamictp</groupId>
8+
<artifactId>dynamic-tp-test-extension</artifactId>
9+
<version>1.1.9.1</version>
10+
<relativePath>../pom.xml</relativePath>
11+
</parent>
12+
13+
<artifactId>test-extension-limiter-redis</artifactId>
14+
15+
<properties>
16+
<maven.compiler.source>8</maven.compiler.source>
17+
<maven.compiler.target>8</maven.compiler.target>
18+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.dromara.dynamictp</groupId>
24+
<artifactId>dynamic-tp-spring-boot-starter-common</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.springframework</groupId>
28+
<artifactId>spring-aspects</artifactId>
29+
<scope>test</scope>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.dromara.dynamictp</groupId>
33+
<artifactId>dynamic-tp-spring-boot-starter-extension-limiter-redis</artifactId>
34+
<scope>test</scope>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.apache.commons</groupId>
38+
<artifactId>commons-pool2</artifactId>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.springframework.boot</groupId>
42+
<artifactId>spring-boot-starter-data-redis</artifactId>
43+
</dependency>
44+
</dependencies>
45+
46+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.dromara.dynamictp.test.extension;
19+
20+
import lombok.val;
21+
import org.dromara.dynamictp.extension.limiter.redis.ratelimiter.RedisRateLimiter;
22+
import org.dromara.dynamictp.spring.EnableDynamicTp;
23+
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.api.extension.ExtendWith;
25+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
26+
import org.springframework.boot.test.context.SpringBootTest;
27+
import org.springframework.test.context.junit.jupiter.SpringExtension;
28+
29+
import javax.annotation.Resource;
30+
import java.util.List;
31+
import java.util.concurrent.TimeUnit;
32+
33+
@SpringBootTest(classes = RedisRateLimiterTest.class)
34+
@ExtendWith(SpringExtension.class)
35+
@EnableAutoConfiguration
36+
@EnableDynamicTp
37+
class RedisRateLimiterTest {
38+
39+
@Resource
40+
private RedisRateLimiter<List<Long>> redisScriptRateLimiter;
41+
42+
@Test
43+
void testRedisRateLimiterCheck() throws InterruptedException {
44+
for (int i = 0; i < 6; i++) {
45+
TimeUnit.SECONDS.sleep(1);
46+
val res = redisScriptRateLimiter.check("rate-limiter", 120, 5);
47+
System.out.println(res);
48+
}
49+
}
50+
51+
}
52+
53+
54+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
spring:
2+
redis:
3+
cluster:
4+
nodes:
5+
- 127.0.0.1:6379
6+
- 127.0.0.1:6379
7+
- 127.0.0.1:6379

0 commit comments

Comments
 (0)