diff --git a/README.md b/README.md
index 0ece196..22657ee 100644
--- a/README.md
+++ b/README.md
@@ -1,56 +1,11 @@
# rate-limiter
-> 基于redis限流系统
-## 使用
-#### 1、引入依赖
+> 限流是对系统的出入流量进行控制,防止大流量出入,导致资源不足,系统不稳定。
-```xml
-
- cn.wukq
- rate-limiter-client
- 1.0-SNAPSHOT
-
+## 描述
+本人基于redis配合lua脚本,使用了自己改进后的令牌桶算法实现的限流系统,对于该系统的设计和实现,有兴趣的可以看一下本人写的文章:[基于Redis的限流系统的设计](https://mp.weixin.qq.com/s?__biz=MzI0MTk0NTY5MA==&mid=2247483711&idx=1&sn=28780c8b26f24ac6314ff5c599bb622c&chksm=e9029c0ade75151c353cd6b720ce438b4342afd8ef3a7d03c61712554c6a000ac3646bbc3124&scene=38#wechat_redirect)
-```
-#### 2、引入applicationContext
-```xml
-
-```
-```
-## spring-config.properties 中加入
-redis.host.user.ratelimiter=127.0.0.1
-redis.port.user.ratelimiter=6379
-redis.database.user.ratelimiter=4
-```
-#### 3、代码中
-
-```java
-@Resource(name = "rateLimiterRedisClient")
-private RedisClient redisClient;
-
-
-/**
-* 这个方法在取令牌过程中,如果redis挂了也算成功
-*
-* 取令牌的数量为默认值:1
-*/
-boolean acquire = redisClient.acquire(key);
-
-
-
-/**
-*
-*
-* 这个方法会返回一个Token 对象,
-* Token对象有详细的描述告知取令牌的状态
-*
-* 取令牌的数量为:tokenCount
-*
-*/
-Token token = redisClient.acquireToken(key,tokenCount);
-
-```
diff --git a/pom.xml b/pom.xml
index 7372f09..8a18db9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,133 +4,28 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- cn.wukq
+ cn.caijiajia
rate-limiter
pom
- 1.0-SNAPSHOT
+ 1.0.0.SNAPSHOT
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.5.6.RELEASE
+
rate-limiter-client
rate-limiter-server
+ rate-limiter-autoConfigure
+ rate-limiter-starter
-
- 4.1.7.RELEASE
- 7.0.65
-
-
-
- org.springframework
- spring-core
-
-
- org.springframework.data
- spring-data-redis
-
-
- redis.clients
- jedis
-
-
- org.projectlombok
- lombok
-
-
- org.apache.commons
- commons-lang3
-
-
- com.google.guava
- guava
-
-
- junit
- junit
-
-
- org.mockito
- mockito-core
-
-
- org.springframework
- spring-test
-
-
+
-
-
- org.springframework
- spring-core
- ${spring.version}
-
-
- org.springframework
- spring-web
- ${spring.version}
-
-
- org.springframework
- spring-webmvc
- ${spring.version}
-
-
- org.springframework
- spring-tx
- ${spring.version}
-
-
- org.springframework
- spring-jdbc
- ${spring.version}
-
-
-
- org.springframework.security
- spring-security-web
- 4.0.2.RELEASE
-
-
- org.springframework.data
- spring-data-redis
- 1.7.4.RELEASE
-
-
- spring-aop
- org.springframework
-
-
- spring-oxm
- org.springframework
-
-
- spring-context-support
- org.springframework
-
-
-
-
- redis.clients
- jedis
- 2.9.0
-
-
-
- org.mybatis
- mybatis
- 3.2.8
-
-
- org.mybatis
- mybatis-spring
- 1.2.2
-
-
-
-
- mysql
- mysql-connector-java
- 5.1.34
- runtime
+ com.alibaba
+ fastjson
+ 1.2.40
com.google.guava
@@ -142,33 +37,31 @@
commons-lang3
3.4
+
+ commons-io
+ commons-io
+ 2.4
+
org.apache.httpcomponents
httpclient
4.3.6
+
- ch.qos.logback
- logback-classic
- 1.1.3
-
-
- org.mockito
- mockito-core
- 1.9.5
- test
+ org.codehaus.janino
+ janino
+ 2.7.8
- org.springframework
- spring-test
- ${spring.version}
- test
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 1.3.1
- com.jayway.jsonpath
- json-path
- 1.2.0
- test
+ redis.clients
+ jedis
+ 2.9.0
junit
@@ -176,47 +69,20 @@
4.12
test
-
- com.alibaba
- fastjson
- 1.2.40
-
org.projectlombok
lombok
- 1.16.10
+ 1.16.18
- org.apache.commons
- commons-lang3
- 3.4
-
-
- com.google.guava
- guava
- 19.0
-
-
- cn.wukq
+ cn.caijiajia
rate-limiter-client
- 1.0-SNAPSHOT
-
-
- org.hibernate
- hibernate-validator
- 5.1.3.Final
-
-
- org.apache.tomcat
- tomcat-dbcp
- ${tomcat.runtime.version}
- runtime
+ 1.0.0.SNAPSHOT
- org.apache.tomcat
- tomcat-jdbc
- ${tomcat.runtime.version}
- runtime
+ cn.caijiajia
+ rate-limiter-autoConfigure
+ 1.0.0.SNAPSHOT
diff --git a/rate-limiter-autoconfigure/pom.xml b/rate-limiter-autoconfigure/pom.xml
new file mode 100644
index 0000000..416e471
--- /dev/null
+++ b/rate-limiter-autoconfigure/pom.xml
@@ -0,0 +1,44 @@
+
+
+
+ rate-limiter
+ cn.caijiajia
+ 1.0.0.SNAPSHOT
+
+ 4.0.0
+
+ rate-limiter-autoConfigure
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+ cn.wukq
+ rate-limiter-client
+
+
+ junit
+ junit
+
+
+ cn.caijiajia
+ rate-limiter-client
+
+
+
\ No newline at end of file
diff --git a/rate-limiter-autoconfigure/src/main/java/cn/caijiajia/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java b/rate-limiter-autoconfigure/src/main/java/cn/caijiajia/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java
new file mode 100644
index 0000000..062a624
--- /dev/null
+++ b/rate-limiter-autoconfigure/src/main/java/cn/caijiajia/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java
@@ -0,0 +1,41 @@
+package cn.caijiajia.ratelimiter.autoconfigure;
+
+import cn.caijiajia.ratelimiter.client.RateLimiterClient;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+
+/**
+ * @author wukaiqiang
+ */
+@Configuration
+@AutoConfigureBefore(RedisAutoConfiguration.class)
+@ConditionalOnBean(StringRedisTemplate.class)
+public class RateLimiterAutoConfiguration {
+
+ private StringRedisTemplate stringRedisTemplate;
+
+ public RateLimiterAutoConfiguration(StringRedisTemplate stringRedisTemplate) {
+ this.stringRedisTemplate = stringRedisTemplate;
+ }
+
+ private DefaultRedisScript rateLimiterLua() {
+ DefaultRedisScript defaultRedisScript = new DefaultRedisScript();
+ defaultRedisScript.setLocation(new ClassPathResource("classpath:rate_limiter.lua"));
+ defaultRedisScript.setResultType(Long.class);
+ return defaultRedisScript;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(name = "rateLimiterClient")
+ public RateLimiterClient rateLimiterClient() {
+ return new RateLimiterClient(stringRedisTemplate, rateLimiterLua());
+ }
+
+}
diff --git a/rate-limiter-client/pom.xml b/rate-limiter-client/pom.xml
index 2d0d08d..ebbe297 100644
--- a/rate-limiter-client/pom.xml
+++ b/rate-limiter-client/pom.xml
@@ -4,8 +4,8 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
rate-limiter
- cn.wukq
- 1.0-SNAPSHOT
+ cn.caijiajia
+ 1.0.0.SNAPSHOT
4.0.0
@@ -13,6 +13,43 @@
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.data
+ spring-data-redis
+
+
+ redis.clients
+ jedis
+
+
+ com.alibaba
+ fastjson
+
+
+ com.google.guava
+ guava
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ commons-io
+ commons-io
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+
+ org.codehaus.janino
+ janino
+
\ No newline at end of file
diff --git a/rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/RateLimiterClient.java b/rate-limiter-client/src/main/java/cn/caijiajia/ratelimiter/client/RateLimiterClient.java
similarity index 72%
rename from rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/RateLimiterClient.java
rename to rate-limiter-client/src/main/java/cn/caijiajia/ratelimiter/client/RateLimiterClient.java
index 1741123..e6b1df9 100644
--- a/rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/RateLimiterClient.java
+++ b/rate-limiter-client/src/main/java/cn/caijiajia/ratelimiter/client/RateLimiterClient.java
@@ -1,4 +1,4 @@
-package cn.wukq.ratelimiter.client;
+package cn.caijiajia.ratelimiter.client;
import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
@@ -6,26 +6,24 @@
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
-import org.springframework.stereotype.Component;
-import org.springframework.util.Assert;
-
-import javax.annotation.Resource;
/**
* @author wukaiqiang
*/
-@Component
public class RateLimiterClient {
private Logger logger = LoggerFactory.getLogger(RateLimiterClient.class);
- @Resource(name = "rateLimiterRedisClient")
- private RedisClient redisClient;
+ private StringRedisTemplate stringRedisTemplate;
- @Resource(name = "rateLimiterLua")
private RedisScript rateLimiterClientLua;
+ public RateLimiterClient(StringRedisTemplate stringRedisTemplate, RedisScript rateLimiterClientLua) {
+ this.stringRedisTemplate = stringRedisTemplate;
+ this.rateLimiterClientLua = rateLimiterClientLua;
+ }
/**
* 获取令牌,访问redis异常算做成功
@@ -36,7 +34,6 @@ public class RateLimiterClient {
* @return
*/
public boolean acquire(String context, String key) {
- Assert.notNull(key);
Token token = acquireToken(context, key);
return token.isPass() || token.isAccessRedisFail();
}
@@ -51,8 +48,6 @@ public boolean acquire(String context, String key) {
* @return
*/
public Token acquireToken(String context, String key) {
- Assert.notNull(context);
- Assert.notNull(key);
return acquireToken(context, key, 1);
}
@@ -65,18 +60,15 @@ public Token acquireToken(String context, String key) {
* @return
*/
public Token acquireToken(String context, String key, Integer permits) {
- Assert.notNull(context);
- Assert.notNull(key);
- Assert.notNull(permits);
Token token;
try {
- Long currMillSecond = redisClient.getRedisTemplate().execute(new RedisCallback() {
+ Long currMillSecond = stringRedisTemplate.execute(new RedisCallback() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.time();
}
});
- Long acquire = redisClient.getRedisTemplate().execute(rateLimiterClientLua, ImmutableList.of(getKey(key)), RateLimiterConstants.RATE_LIMITER_ACQUIRE_METHOD, permits.toString(), currMillSecond.toString(), context);
+ Long acquire = stringRedisTemplate.execute(rateLimiterClientLua, ImmutableList.of(getKey(key)), RateLimiterConstants.RATE_LIMITER_ACQUIRE_METHOD, permits.toString(), currMillSecond.toString(), context);
if (acquire == 1) {
token = Token.PASS;
diff --git a/rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/RateLimiterConstants.java b/rate-limiter-client/src/main/java/cn/caijiajia/ratelimiter/client/RateLimiterConstants.java
similarity index 89%
rename from rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/RateLimiterConstants.java
rename to rate-limiter-client/src/main/java/cn/caijiajia/ratelimiter/client/RateLimiterConstants.java
index f93f885..80090e7 100644
--- a/rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/RateLimiterConstants.java
+++ b/rate-limiter-client/src/main/java/cn/caijiajia/ratelimiter/client/RateLimiterConstants.java
@@ -1,4 +1,4 @@
-package cn.wukq.ratelimiter.client;
+package cn.caijiajia.ratelimiter.client;
/**
* @author wukaiqiang
diff --git a/rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/Token.java b/rate-limiter-client/src/main/java/cn/caijiajia/ratelimiter/client/Token.java
similarity index 94%
rename from rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/Token.java
rename to rate-limiter-client/src/main/java/cn/caijiajia/ratelimiter/client/Token.java
index 1609fa6..92bc282 100644
--- a/rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/Token.java
+++ b/rate-limiter-client/src/main/java/cn/caijiajia/ratelimiter/client/Token.java
@@ -1,4 +1,4 @@
-package cn.wukq.ratelimiter.client;
+package cn.caijiajia.ratelimiter.client;
/**
* @author wukaiqiang
diff --git a/rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/RedisClient.java b/rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/RedisClient.java
deleted file mode 100644
index 13c5710..0000000
--- a/rate-limiter-client/src/main/java/cn/wukq/ratelimiter/client/RedisClient.java
+++ /dev/null
@@ -1,296 +0,0 @@
-package cn.wukq.ratelimiter.client;
-
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
-import org.springframework.data.redis.core.StringRedisTemplate;
-import org.springframework.data.redis.core.script.RedisScript;
-import org.springframework.data.redis.serializer.StringRedisSerializer;
-import redis.clients.jedis.JedisPoolConfig;
-
-import java.util.*;
-
-/**
- * A wrapper of the Spring redis client.
- *
- * @author wukaiqiang
- */
-public class RedisClient implements InitializingBean {
-
- private String hostName;
- private int port;
- private String password;
- private int database = 0;
- private boolean usePool = true;
- private int maxConnTotal = 100;
- private int maxConnIdle = 10;
-
- private static Logger logger = LoggerFactory.getLogger(RedisClient.class);
-
- private StringRedisTemplate redisTemplate;
-
- @Autowired
- private RedisScript> redisScript;
-
- /* (non-Javadoc)
- * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
- */
- @Override
- public void afterPropertiesSet() throws Exception {
- JedisConnectionFactory connectionFactory = null;
- if (usePool) {
- JedisPoolConfig poolConfig = new JedisPoolConfig();
- poolConfig.setMaxTotal(maxConnTotal);
- poolConfig.setMaxIdle(maxConnIdle);
- connectionFactory = new JedisConnectionFactory(poolConfig);
- } else {
- connectionFactory = new JedisConnectionFactory();
- }
- connectionFactory.setHostName(hostName);
- connectionFactory.setPort(port);
- if (StringUtils.isNotBlank(password)) {
- connectionFactory.setPassword(password);
- }
- connectionFactory.setDatabase(database);
- connectionFactory.afterPropertiesSet();
-
- redisTemplate = new StringRedisTemplate();
- redisTemplate.setConnectionFactory(connectionFactory);
- redisTemplate.setDefaultSerializer(new StringRedisSerializer());
- redisTemplate.afterPropertiesSet();
- }
-
- public StringRedisTemplate getRedisTemplate() {
- return redisTemplate;
- }
-
- /* 原子计数器 */
- public Long increment(String key, long i) {
- return redisTemplate.opsForValue().increment(key, i);
- }
-
- /* Key commands */
- public void delete(String key) {
- redisTemplate.delete(key);
- }
-
- public void delete(String... keys) {
- redisTemplate.delete(Arrays.asList(keys));
- }
-
- public Set keys(String pattern) {
- return redisTemplate.keys(pattern);
- }
- /* End of Key commands */
-
- /* Value commands */
- public void set(String key, String value) {
- redisTemplate.opsForValue().set(key, value);
- }
-
- public String get(String key) {
- return redisTemplate.opsForValue().get(key);
- }
- /* End of Value commands */
-
- /* Set commands */
- public long opsForSetAdd(String key, String... members) {
- return redisTemplate.opsForSet().add(key, members);
- }
-
- public Set opsForSetIntersect(String key1, String key2) {
- return redisTemplate.opsForSet().intersect(key1, key2);
- }
-
- public Long opsForSetIntersectAndStore(String destKey, String key1, String key2) {
- return redisTemplate.opsForSet().intersectAndStore(key1, key2, destKey);
- }
-
- public Set opsForSetUnion(String key1, String key2) {
- return redisTemplate.opsForSet().union(key1, key2);
- }
-
- public Long opsForSetUnionAndStore(String destKey, String key1, String key2) {
- return redisTemplate.opsForSet().unionAndStore(key1, key2, destKey);
- }
-
- public Set opsForSetDifference(String key1, String key2) {
- return redisTemplate.opsForSet().difference(key1, key2);
- }
-
- public Long opsForSetDifferenceAndStore(String destKey, String key1, String key2) {
- return redisTemplate.opsForSet().differenceAndStore(key1, key2, destKey);
- }
-
- public Long opsForSetSize(String key) {
- return redisTemplate.opsForSet().size(key);
- }
-
- public Set opsForSetMembers(String key) {
- return redisTemplate.opsForSet().members(key);
- }
- /* Ends of Set commands */
-
- /* SortedSet commands */
- public Set opsForSortedSetMembers(String key) {
- long setLength = redisTemplate.opsForZSet().size(key);
- return redisTemplate.opsForZSet().range(key, 0, setLength);
- }
- /* Ends of SortedSet commands */
-
- /* Hash commands */
- public Map opsForHashEntries(String key) {
- Map entries = new HashMap<>();
- Map