├── .gitignore ├── src ├── test │ ├── resources │ │ ├── test-redis_config.properties │ │ └── test-rate-limiter.properties │ └── java │ │ └── com │ │ └── github │ │ └── cpthack │ │ └── commons │ │ └── ratelimiter │ │ ├── config │ │ ├── CustomRateLimiterConfig.java │ │ └── RateRedisConfig.java │ │ ├── limiter │ │ └── LimiterTest.java │ │ └── lock │ │ └── LockTest.java └── main │ ├── resources │ ├── rate-limiter.properties │ └── log4j.properties │ └── java │ └── com │ └── github │ └── cpthack │ └── commons │ └── ratelimiter │ ├── lock │ ├── LockFactory.java │ ├── Lock.java │ ├── SingleLock.java │ └── DistributedLock.java │ ├── limiter │ ├── LimiterFactory.java │ ├── Limiter.java │ ├── SingleLimiter.java │ └── DistributedLimiter.java │ ├── constants │ └── RateLimiterConstants.java │ ├── bean │ ├── LimiterBean.java │ └── LockBean.java │ ├── utils │ └── ProxyHelper.java │ ├── config │ ├── AbstractConfig.java │ ├── RateLimiterConfig.java │ └── FileUtils.java │ └── base │ └── AbstractBaseFactory.java ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings/ 3 | /.classpath 4 | /.project 5 | /logs/ 6 | -------------------------------------------------------------------------------- /src/test/resources/test-redis_config.properties: -------------------------------------------------------------------------------- 1 | 2 | ##===REDIS服务器信息=== 3 | 4 | ##服务器地址 5 | redis.server.ip=127.0.0.1 6 | 7 | ##服务器端口 8 | redis.server.port=6379 9 | 10 | ##密码(如不设置请留空) 11 | redis.server.password= 12 | 13 | ##===REDIS连接配置信息=== 14 | 15 | ##客户端连接池的大小 16 | redis.client.pool.max=50000 17 | 18 | ##客户端连接池的空闲数 19 | redis.client.pool.idle=10 20 | 21 | ##客户端连接池TestOnBorrow 22 | redis.client.pool.TestOnBorrow=false 23 | 24 | ##客户端连接池尝试连接超时毫秒数(msec) 25 | redis.client.pool.try.timeout=500 26 | 27 | -------------------------------------------------------------------------------- /src/test/resources/test-rate-limiter.properties: -------------------------------------------------------------------------------- 1 | #限流相关配置 2 | 3 | #限流开关 4 | rate.limiter.switch=true; 5 | 6 | #并发控制相关逻辑配置(该模块主要限制逻辑并发量,不允许超过配置中的并发量被下发,比如:最高不超过同时10次并发请求) 7 | #并发控制路由地址1 8 | rate.limiter.lock.router.1=/lock1 9 | #并发控制锁失效时间(该配置项只对分布式并发锁有效,单机锁可不配置) 10 | rate.limiter.lock.exprie.time.1=1 11 | #并发控制并发量 12 | rate.limiter.lock.permits.count.1=11 13 | 14 | #并发任务总数 15 | rate.limiter.lock.task.count=100 16 | 17 | 18 | #限流相关逻辑配置(该模块主要限制调用频率,不允许超过配置中的请求频率被下发,比如:不超过10次请求每秒被下发) 19 | #设置需要限流的路由地址,暂时只支持精准匹配 20 | rate.limiter.limiter.router.1=/limiter1 21 | #设置限流时间频率,单位为 s。默认为0,表示不限时间,达到请求数之后就不再接受请求 22 | rate.limiter.limiter.time.1=1 23 | #设置限流的数量,表示${rate.limiter.limiter.time.x}秒内可以访问的总次数:${rate.limiter.limiter.count.x} 24 | rate.limiter.limiter.count.1=10 25 | 26 | #限流任务总数 27 | rate.limiter.limiter.task.count=100 28 | -------------------------------------------------------------------------------- /src/main/resources/rate-limiter.properties: -------------------------------------------------------------------------------- 1 | #限流相关配置 2 | 3 | #限流开关 4 | rate.limiter.switch=true; 5 | 6 | #并发控制相关逻辑配置(该模块主要限制逻辑并发量,不允许超过配置中的并发量被下发,比如:最高不超过同时10次并发请求) 7 | #并发控制路由地址1 8 | rate.limiter.lock.router.1=/lock1 9 | #并发控制锁失效时间(该配置项只对分布式并发锁有效,单机锁可不配置) 10 | rate.limiter.lock.exprie.time.1=/lock1 11 | #并发控制并发量 12 | rate.limiter.lock.permits.count.1=/lock1 13 | 14 | #并发任务总数 15 | rate.limiter.lock.task.count=100 16 | 17 | 18 | #限流相关逻辑配置(该模块主要限制调用频率,不允许超过配置中的请求频率被下发,比如:不超过10次请求每秒被下发) 19 | #设置需要限流的路由地址,暂时只支持精准匹配 20 | rate.limiter.limiter.router.1=/limiter1 21 | #设置限流时间频率,单位为 s。默认为0,表示不限时间,达到请求数之后就不再接受请求 22 | rate.limiter.limiter.time.1=1 23 | #设置限流的数量,表示${rate.limiter.limiter.time.x}秒内可以访问的总次数:${rate.limiter.limiter.count.x} 24 | rate.limiter.limiter.count.1=10 25 | 26 | #限流任务总数 27 | rate.limiter.limiter.task.count=100 28 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=WARN,error-log,console-log 2 | log4j.logger.com.github=DEBUG,debug-log 3 | 4 | 5 | log4j.appender.error-log=org.apache.log4j.DailyRollingFileAppender 6 | log4j.appender.error-log.File=logs/all_error.log 7 | log4j.appender.error-log.Threshold=ERROR 8 | log4j.appender.error-log.layout=org.apache.log4j.PatternLayout 9 | log4j.appender.error-log.layout.ConversionPattern=[%5p %t %d{yyyy-MM-dd HH:mm:ss,SSS} --%r(ms)]%n[%l]%n %m %n%n 10 | log4j.appender.error-log.DatePattern=yyyy-MM-dd'.log' 11 | 12 | log4j.appender.console-log=org.apache.log4j.ConsoleAppender 13 | log4j.appender.console-log.layout=org.apache.log4j.PatternLayout 14 | log4j.appender.console-log.layout.ConversionPattern=[%5p %t %d{yyyy-MM-dd HH:mm:ss,SSS} --%r(ms)]%n[%l]%n %m %n%n 15 | 16 | log4j.appender.debug-log=org.apache.log4j.DailyRollingFileAppender 17 | log4j.appender.debug-log.File=logs/jiuwei_debug.log 18 | log4j.appender.debug-log.Threshold=DEBUG 19 | log4j.appender.debug-log.layout=org.apache.log4j.PatternLayout 20 | log4j.appender.debug-log.layout.ConversionPattern=[%5p %t %d{yyyy-MM-dd HH:mm:ss,SSS} --%r(ms)]%n[%l]%n %m %n%n 21 | log4j.appender.debug-log.DatePattern=yyyy-MM-dd'.log' 22 | -------------------------------------------------------------------------------- /src/test/java/com/github/cpthack/commons/ratelimiter/config/CustomRateLimiterConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.config; 17 | 18 | /** 19 | * CustomRateLimiterConfig.java
20 | * 21 | *
22 |  * 自定义限流配置类
23 |  * 
24 | * 25 | * @author cpthack cpt@jianzhimao.com 26 | * @date May 18, 2017 11:26:46 PM 27 | * @since JDK 1.7 28 | */ 29 | public class CustomRateLimiterConfig extends RateLimiterConfig { 30 | 31 | private final String FILE_NAME = "test-rate-limiter.properties"; 32 | 33 | @Override 34 | public String getConfigFile() { 35 | return FILE_NAME; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/github/cpthack/commons/ratelimiter/config/RateRedisConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.config; 17 | 18 | import com.cpthack.commons.rdclient.config.RedisConfig; 19 | 20 | /** 21 | * RateRedisConfig.java
22 | * 23 | *
24 |  * 自定义Redis配置类
25 |  * 
26 | * 27 | * @author cpthack cpt@jianzhimao.com 28 | * @date May 18, 2017 11:20:56 PM 29 | * @since JDK 1.7 30 | */ 31 | public class RateRedisConfig extends RedisConfig { 32 | 33 | private final String FILE_NAME = "test-redis_config.properties"; 34 | 35 | @Override 36 | public String getConfigFile() { 37 | return FILE_NAME; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/lock/LockFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.lock; 17 | 18 | import com.github.cpthack.commons.ratelimiter.base.AbstractBaseFactory; 19 | 20 | /** 21 | * LockFactory.java
22 | * 23 | *
24 |  * 并发锁工厂类
25 |  * 
26 | * 27 | * @author cpthack cpt@jianzhimao.com 28 | * @date May 24, 2017 1:07:13 PM 29 | * @since JDK 1.7 30 | */ 31 | public class LockFactory extends AbstractBaseFactory { 32 | private final static LockFactory factory = new LockFactory(); 33 | 34 | public LockFactory() { 35 | super(SingleLock.class, DistributedLock.class); 36 | } 37 | 38 | public static LockFactory getInstance() { 39 | return factory; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/limiter/LimiterFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.limiter; 17 | 18 | import com.github.cpthack.commons.ratelimiter.base.AbstractBaseFactory; 19 | 20 | /** 21 | * LimiterFactory.java
22 | * 23 | *
24 |  * 限流工厂类
25 |  * 
26 | * 27 | * @author cpthack cpt@jianzhimao.com 28 | * @date May 17, 2017 12:18:47 AM 29 | * @since JDK 1.7 30 | */ 31 | public class LimiterFactory extends AbstractBaseFactory { 32 | 33 | private final static LimiterFactory factory = new LimiterFactory(); 34 | 35 | public LimiterFactory() { 36 | super(SingleLimiter.class, DistributedLimiter.class); 37 | } 38 | 39 | public static LimiterFactory getInstance() { 40 | return factory; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/constants/RateLimiterConstants.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.constants; 17 | 18 | /** 19 | * RateLimiterConstants.java
20 | * 21 | *
22 |  * 常量配置类
23 |  * 
24 | * 25 | * @author cpthack cpt@jianzhimao.com 26 | * @date May 16, 2017 4:39:24 PM 27 | * @since JDK 1.7 28 | */ 29 | public class RateLimiterConstants { 30 | 31 | /** 32 | * 全局默认的配置文件名称 33 | */ 34 | public final static String RATE_LIMITER_CONFIG_FILE = "rate-limiter.properties"; 35 | 36 | /** 37 | * 限流控制-默认的限流任务总数 38 | */ 39 | public final static int MAX_RATE_LIMITER_LIMITER_LIMIT = 10; 40 | 41 | /** 42 | * 限流控制-默认的并发控制任务总数 43 | */ 44 | public final static int MAX_RATE_LIMITER_LOCK_LIMIT = 10; 45 | 46 | /** 47 | * 并发锁-默认的许可数量 48 | */ 49 | public final static int LOCK_DEFAULT_PERMITS_NUM = 1; 50 | 51 | /** 52 | * 并发锁-默认的锁失效时间,单位秒 53 | */ 54 | public final static int LOCK_DEFAULT_EXPIRE_TIME = 3; 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/bean/LimiterBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.bean; 17 | 18 | /** 19 | * LimiterBean.java
20 | * 21 | *
22 |  * Limiter限流的JavaBean对象
23 |  * 表示:在${time} 秒内允许通过${count}次访问
24 |  * 
25 | * 26 | * @author cpthack cpt@jianzhimao.com 27 | * @date May 16, 2017 9:40:08 PM 28 | * @since JDK 1.7 29 | */ 30 | public class LimiterBean { 31 | 32 | private String router; 33 | 34 | private int time; 35 | 36 | private int count; 37 | 38 | /** 39 | * 限流的路由地址 40 | */ 41 | public String getRouter() { 42 | return router; 43 | } 44 | 45 | public void setRouter(String router) { 46 | this.router = router; 47 | } 48 | 49 | /** 50 | * 限流时间,单位为 秒(s) 51 | */ 52 | public int getTime() { 53 | return time; 54 | } 55 | 56 | public void setTime(int time) { 57 | this.time = time; 58 | } 59 | 60 | /** 61 | * 限流数量 62 | */ 63 | public int getCount() { 64 | return count; 65 | } 66 | 67 | public void setCount(int count) { 68 | this.count = count; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/bean/LockBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.bean; 17 | 18 | /** 19 | * LockBean.java
20 | * 21 | *
22 |  * Limiter并发锁的JavaBean对象
23 |  * 
24 | * 25 | * @author cpthack cpt@jianzhimao.com 26 | * @date May 22, 2017 11:09:31 AM 27 | * @since JDK 1.7 28 | */ 29 | public class LockBean { 30 | 31 | private String uniqueKey; 32 | 33 | private int expireTime; 34 | 35 | private int permits; 36 | 37 | /** 38 | * 加锁唯一键标识 39 | */ 40 | public String getUniqueKey() { 41 | return uniqueKey; 42 | } 43 | 44 | public void setUniqueKey(String uniqueKey) { 45 | this.uniqueKey = uniqueKey; 46 | } 47 | 48 | /** 49 | * 过期时间,单位秒 50 | */ 51 | public int getExpireTime() { 52 | return expireTime; 53 | } 54 | 55 | public void setExpireTime(int expireTime) { 56 | this.expireTime = expireTime; 57 | } 58 | 59 | /** 60 | * 许可数量(最大并发量,默认为1) 61 | */ 62 | public int getPermits() { 63 | return permits; 64 | } 65 | 66 | public void setPermits(int permits) { 67 | this.permits = permits; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/utils/ProxyHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.utils; 17 | 18 | /** 19 | * ProxyHelper.java
20 | * 21 | *
22 |  * 代理工具类,动态生成类实例
23 |  * 
24 | * 25 | * @author cpthack cpt@jianzhimao.com 26 | * @date May 24, 2017 12:24:17 PM 27 | * @since JDK 1.7 28 | */ 29 | public class ProxyHelper { 30 | 31 | @SuppressWarnings({ "rawtypes", "unchecked" }) 32 | public static T getInstance(Class target, Class[] args, Object[] argsValue) { 33 | try { 34 | Class c = Class.forName(target.getName()); 35 | java.lang.reflect.Constructor constructor = c.getConstructor(args); 36 | return (T) constructor.newInstance(argsValue); 37 | } 38 | catch (Exception e) { 39 | e.printStackTrace(); 40 | } 41 | return null; 42 | } 43 | 44 | @SuppressWarnings({ "rawtypes", "unchecked" }) 45 | public static T getInstance(Class target) { 46 | try { 47 | Class c = Class.forName(target.getClass().getName()); 48 | return (T) c.newInstance(); 49 | } 50 | catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | return null; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.github.cpthack.commons 5 | java-rate-limiter 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 10 | log4j 11 | log4j 12 | 1.2.17 13 | 14 | 15 | 16 | org.slf4j 17 | slf4j-api 18 | 1.7.24 19 | 20 | 21 | 22 | org.slf4j 23 | slf4j-log4j12 24 | 1.7.21 25 | 26 | 27 | 28 | com.google.guava 29 | guava 30 | 19.0 31 | 32 | 33 | org.apache.commons 34 | commons-lang3 35 | 3.4 36 | 37 | 38 | 39 | com.github.cpthack.commons 40 | redis-client 41 | 1.0 42 | 43 | 44 | 45 | 46 | 47 | 48 | maven-compiler-plugin 49 | 50 | 1.7 51 | 1.7 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/config/AbstractConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.cpthack.commons.ratelimiter.config; 2 | 3 | import java.util.Properties; 4 | 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * 11 | * AbstractConfig.java
12 | * 13 | *
14 |  * 基础配置加载抽象类
15 |  * 
16 | * 17 | * @author cpthack cpt@jianzhimao.com 18 | * @date May 16, 2017 5:00:31 PM 19 | * @since JDK 1.7 20 | */ 21 | public abstract class AbstractConfig { 22 | private static Logger logger = LoggerFactory.getLogger(AbstractConfig.class); 23 | private Properties configProperties = null; 24 | 25 | public synchronized boolean reloadConfig() { 26 | logger.debug("Base config reloadConfig()."); 27 | if (configProperties != null) { 28 | configProperties.clear(); 29 | } 30 | String file = getConfigFile(); 31 | configProperties = FileUtils.loadStaticProperties(file); 32 | if (configProperties == null) { 33 | return false; 34 | } 35 | return true; 36 | } 37 | 38 | public String getProperty(String key) { 39 | if (configProperties == null) { 40 | return null; 41 | } 42 | return configProperties.getProperty(key); 43 | } 44 | 45 | public String getProperty(String key, String defaultV) { 46 | String strValue = defaultV; 47 | String temp = getProperty(key); 48 | if (StringUtils.isNotBlank(temp)) { 49 | strValue = temp; 50 | } 51 | return strValue; 52 | } 53 | 54 | public int getPropertyToInt(String key, int defaultV) { 55 | int intValue = defaultV; 56 | String temp = getProperty(key); 57 | try { 58 | intValue = Integer.parseInt(temp); 59 | } 60 | catch (NumberFormatException e) { 61 | } 62 | return intValue; 63 | } 64 | 65 | // 子类需指定配置文件名 66 | public abstract String getConfigFile(); 67 | } -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/limiter/Limiter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.limiter; 17 | 18 | /** 19 | * Limiter.java
20 | * 21 | *
22 |  * 限流接口类
23 |  * 
24 | * 25 | * @author cpthack cpt@jianzhimao.com 26 | * @date May 16, 2017 5:54:57 PM 27 | * @since JDK 1.7 28 | */ 29 | public interface Limiter { 30 | 31 | /** 32 | * 33 | * execute
34 | *
35 | * 36 | * 执行限流控制,如果通过则返回true,如果不通过则返回false。
37 | * 38 | * @author cpthack cpt@jianzhimao.com 39 | * @param routerName 40 | * 路由名称 41 | * @return boolean 42 | * 43 | */ 44 | boolean execute(String routerName); 45 | 46 | /** 47 | * 48 | * execute
49 | *
50 | * 51 | * 执行限流控制,如果通过则返回true,如果不通过则返回false。 如果限流规则不存在,则往规则集合中添加当前限流规则
52 | * 53 | * @author cpthack cpt@jianzhimao.com 54 | * @param routerName 55 | * 路由名称 56 | * @param limitCount 57 | * 限流数量 58 | * @return boolean 59 | * 60 | */ 61 | boolean execute(String routerName, int limitCount); 62 | 63 | /** 64 | * 65 | * execute
66 | *
67 | * 68 | * 执行限流控制,如果通过则返回true,如果不通过则返回false。 如果限流规则不存在,则往规则集合中添加当前限流规则
69 | * 70 | * @author cpthack cpt@jianzhimao.com 71 | * @param routerName 72 | * 路由名称 73 | * @param limitCount 74 | * 限流数量 75 | * @param time 76 | * 限流时间,单位是秒 77 | * @return boolean 78 | * 79 | */ 80 | boolean execute(String routerName, int limitCount, int time); 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/lock/Lock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.lock; 17 | 18 | /** 19 | * Lock.java
20 | * 21 | *
22 |  * 并发锁接口类
23 |  * 
24 | * 25 | * @author cpthack cpt@jianzhimao.com 26 | * @date May 21, 2017 9:59:03 AM 27 | * @since JDK 1.7 28 | */ 29 | public interface Lock { 30 | 31 | /** 32 | * 33 | * lock
34 | *
35 | * 36 | * 加锁
37 | * 38 | * @author cpthack cpt@jianzhimao.com 39 | * @param uniqueKey 40 | * 加锁唯一键标识 41 | * @return 加锁/获取锁成功则返回true,否则返回false 42 | * 43 | */ 44 | boolean lock(String uniqueKey); 45 | 46 | /** 47 | * 48 | * lock
49 | *
50 | * 51 | * 加锁
52 | * 53 | * @author cpthack cpt@jianzhimao.com 54 | * @param uniqueKey 55 | * 加锁唯一键标识 56 | * @param expireTime 57 | * 过期时间,单位秒 58 | * @return 加锁/获取锁成功则返回true,否则返回false 59 | * 60 | */ 61 | boolean lock(String uniqueKey, int expireTime); 62 | 63 | /** 64 | * 65 | * lock
66 | *
67 | * 68 | * 加锁
69 | * 70 | * @author cpthack cpt@jianzhimao.com 71 | * @param uniqueKey 72 | * 加锁唯一键标识 73 | * @param expireTime 74 | * 过期时间,单位秒 75 | * @param permits 76 | * 许可数量(最大并发量,默认为1) 77 | * @return 加锁/获取锁成功则返回true,否则返回false 78 | * 79 | */ 80 | boolean lock(String uniqueKey, int expireTime, int permits); 81 | 82 | /** 83 | * 84 | * releaseLock
85 | *
86 | * 87 | * 释放锁
88 | * 89 | * @author cpthack cpt@jianzhimao.com 90 | * @param uniqueKey 91 | * 加锁唯一键标识 92 | * @return boolean 93 | * 94 | */ 95 | boolean releaseLock(String uniqueKey); 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/github/cpthack/commons/ratelimiter/limiter/LimiterTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.limiter; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import com.cpthack.commons.rdclient.config.RedisConfig; 22 | import com.github.cpthack.commons.ratelimiter.config.CustomRateLimiterConfig; 23 | import com.github.cpthack.commons.ratelimiter.config.RateLimiterConfig; 24 | import com.github.cpthack.commons.ratelimiter.config.RateRedisConfig; 25 | 26 | /** 27 | * LimiterTest.java
28 | * 29 | *
 30 |  * 限流测试类
 31 |  * 
32 | * 33 | * @author cpthack cpt@jianzhimao.com 34 | * @date May 18, 2017 11:13:25 PM 35 | * @since JDK 1.7 36 | */ 37 | public class LimiterTest { 38 | private final static Logger logger = LoggerFactory.getLogger(LimiterTest.class); 39 | private static Limiter limiter = null; 40 | private volatile static int maxNum = 0; 41 | private final static String ROUTER_NAME = "/thread-test"; 42 | 43 | public static void main(String[] args) { 44 | RedisConfig redisConfig = new RateRedisConfig(); 45 | RateLimiterConfig rateLimiterConfig = new CustomRateLimiterConfig(); 46 | 47 | singleLimiter(rateLimiterConfig);// 实例化单机限流对象 48 | 49 | // DistributedLimiter(rateLimiterConfig, redisConfig); // 示例化分布式限流对象 50 | 51 | simulateConcurrentThread(); // 模拟并发线程 52 | } 53 | 54 | private static void simulateConcurrentThread() { 55 | DoThing dt = null; 56 | Thread t = null; 57 | for (int i = 0; i < 6; i++) { 58 | dt = new DoThing("Thread " + i); 59 | t = new Thread(dt); 60 | t.start(); 61 | } 62 | } 63 | 64 | private static void singleLimiter(RateLimiterConfig rateLimiterConfig) { 65 | limiter = LimiterFactory.getInstance().single(rateLimiterConfig); 66 | } 67 | 68 | private static void DistributedLimiter(RateLimiterConfig rateLimiterConfig, RedisConfig redisConfig) { 69 | limiter = LimiterFactory.getInstance().distributed(rateLimiterConfig, redisConfig); 70 | } 71 | 72 | /** 73 | * 自定义线程,用于模拟并发 74 | */ 75 | static class DoThing implements Runnable { 76 | String name; 77 | 78 | public DoThing(String name) { 79 | this.name = name; 80 | } 81 | 82 | @SuppressWarnings("static-access") 83 | @Override 84 | public void run() { 85 | try { 86 | for (int i = 0; i < 20; i++) { 87 | 88 | if (!limiter.execute(ROUTER_NAME, 4, 1)) {// 进行限流控制 89 | 90 | logger.info("Thread Name is [{}],调用频率太高了.", name); 91 | Thread.currentThread().sleep(1000); 92 | continue; 93 | } 94 | maxNum++; 95 | logger.info("Thread Name is [{}],最新maxNum的值 = [" + maxNum + "]", name); 96 | } 97 | } 98 | catch (InterruptedException e) { 99 | logger.error("Thread Name [{}] is Error.", name, e); 100 | } 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/test/java/com/github/cpthack/commons/ratelimiter/lock/LockTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.lock; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import com.cpthack.commons.rdclient.config.RedisConfig; 22 | import com.github.cpthack.commons.ratelimiter.config.CustomRateLimiterConfig; 23 | import com.github.cpthack.commons.ratelimiter.config.RateLimiterConfig; 24 | import com.github.cpthack.commons.ratelimiter.config.RateRedisConfig; 25 | 26 | /** 27 | * LockTest.java
28 | * 29 | *
 30 |  * 并发锁测试类
 31 |  * 
32 | * 33 | * @author cpthack cpt@jianzhimao.com 34 | * @date May 22, 2017 10:37:45 AM 35 | * @since JDK 1.7 36 | */ 37 | public class LockTest { 38 | 39 | private final static Logger logger = LoggerFactory.getLogger(LockTest.class); 40 | 41 | private static String UNIQUE_KEY = "/lock1"; 42 | private static Lock lock = null; 43 | 44 | private volatile static int successNum = 0; 45 | 46 | public static void main(String[] args) { 47 | RateLimiterConfig rateLimiterConfig = new CustomRateLimiterConfig(); 48 | // lock = getSingleLock(rateLimiterConfig); 49 | RedisConfig redisConfig = new RateRedisConfig(); 50 | 51 | lock = getDistributedLock(rateLimiterConfig, redisConfig); 52 | 53 | // RedisClientFactory.getClient(redisConfig).set("/lock1", "10");// 54 | // 模拟releaseLock没有执行导致的缓存中存在较多正数值得锁KEY 55 | 56 | simulateConcurrentThread(80); // 模拟并发线程 57 | } 58 | 59 | private static Lock getSingleLock(RateLimiterConfig rateLimiterConfig) { 60 | return LockFactory.getInstance().single(rateLimiterConfig); 61 | } 62 | 63 | private static Lock getDistributedLock(RateLimiterConfig rateLimiterConfig, RedisConfig redisConfig) { 64 | return LockFactory.getInstance().distributed(rateLimiterConfig, redisConfig); 65 | } 66 | 67 | private static void simulateConcurrentThread(int threadNum) { 68 | DoThing dt = null; 69 | Thread t = null; 70 | for (int i = 0; i < threadNum; i++) { 71 | dt = new DoThing("Thread " + i); 72 | t = new Thread(dt); 73 | try { 74 | Thread.sleep(100); 75 | } 76 | catch (InterruptedException e) { 77 | e.printStackTrace(); 78 | }// 模拟程序执行时间 79 | t.start(); 80 | } 81 | } 82 | 83 | /** 84 | * 自定义线程,用于模拟并发 85 | */ 86 | static class DoThing implements Runnable { 87 | String name; 88 | 89 | public DoThing(String name) { 90 | this.name = name; 91 | } 92 | 93 | @SuppressWarnings("static-access") 94 | @Override 95 | public void run() { 96 | try { 97 | if (lock.lock(UNIQUE_KEY)) {// 进行并发控制 98 | 99 | logger.info("Thread Name is [{}] 成功获得锁,正在处理中...", name); 100 | 101 | successNum++; 102 | logger.info("当前成功并发数successNum的值为 [" + successNum + "]"); 103 | Thread.currentThread().sleep(2000);// 模拟程序执行时间 104 | 105 | successNum--; 106 | lock.releaseLock(UNIQUE_KEY); 107 | } 108 | else { 109 | logger.warn("Thread Name is [{}] 尝试获得锁失败", name); 110 | } 111 | } 112 | catch (InterruptedException e) { 113 | logger.error("Thread Name [{}] is Error.", name, e); 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/lock/SingleLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.lock; 17 | 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.concurrent.Semaphore; 22 | 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import com.github.cpthack.commons.ratelimiter.bean.LockBean; 27 | import com.github.cpthack.commons.ratelimiter.config.RateLimiterConfig; 28 | import com.github.cpthack.commons.ratelimiter.constants.RateLimiterConstants; 29 | 30 | /** 31 | * SingleLock.java
32 | * 33 | *
 34 |  * 单机版并发锁 实现类
 35 |  * 
36 | * 37 | * @author cpthack cpt@jianzhimao.com 38 | * @date May 22, 2017 1:31:31 AM 39 | * @since JDK 1.7 40 | */ 41 | public class SingleLock implements Lock { 42 | 43 | private final static Logger logger = LoggerFactory.getLogger(SingleLock.class); 44 | private static Map lockMap = null; 45 | 46 | public SingleLock() { 47 | this(null); 48 | } 49 | 50 | public SingleLock(RateLimiterConfig rateLimiterConfig) { 51 | if (null == rateLimiterConfig) { 52 | rateLimiterConfig = new RateLimiterConfig(); 53 | } 54 | initLockMap(rateLimiterConfig); 55 | } 56 | 57 | protected void initLockMap(RateLimiterConfig rateLimiterConfig) { 58 | if (null != lockMap) 59 | return; 60 | lockMap = new HashMap(); 61 | List lockList = rateLimiterConfig.getLockList(); 62 | Semaphore semaphore = null; 63 | for(LockBean lockBean : lockList){ 64 | semaphore = new Semaphore(lockBean.getPermits()); 65 | lockMap.put(lockBean.getUniqueKey(), semaphore); 66 | logger.debug("单机并发锁-加载并发配置>>>uniqueKey = [{}],time = [{}],count = [{}]",lockBean.getUniqueKey(), lockBean.getExpireTime(), lockBean.getPermits()); 67 | } 68 | } 69 | 70 | @Override 71 | public boolean lock(String uniqueKey) { 72 | return lock(uniqueKey, RateLimiterConstants.LOCK_DEFAULT_EXPIRE_TIME); 73 | } 74 | 75 | @Override 76 | public boolean lock(String uniqueKey, int expireTime) { 77 | return lock(uniqueKey, expireTime, RateLimiterConstants.LOCK_DEFAULT_PERMITS_NUM); 78 | } 79 | 80 | @Override 81 | public boolean lock(String uniqueKey, int expireTime, int permits) { 82 | Semaphore semaphore = lockMap.get(uniqueKey); 83 | 84 | if (null != semaphore) 85 | return semaphore.tryAcquire(); 86 | 87 | boolean isGetLock = dynamicAddLock(uniqueKey, expireTime, permits); 88 | 89 | return isGetLock; 90 | } 91 | 92 | /** 93 | * 94 | * dynamicAddLock
95 | *
96 | * 97 | * 动态添加锁配置
98 | * 当多个线程同时进行动态配置时会发生并发问题,所以需要利用常量池特性[ uniqueKey.intern() ]进行仅同一路由加锁。 99 | * 100 | * @author cpthack cpt@jianzhimao.com 101 | * @param uniqueKey 102 | * 加锁唯一键标识 103 | * @param expireTime 104 | * 过期时间,单位秒 105 | * @param permits 106 | * 许可数量(最大并发量,默认为1) 107 | * @return 加锁/获取锁成功则返回true,否则返回false 108 | * 109 | */ 110 | public boolean dynamicAddLock(String uniqueKey, int expireTime, int permits) { 111 | synchronized (uniqueKey.intern()) { 112 | Semaphore semaphore = lockMap.get(uniqueKey); 113 | if (null != semaphore) 114 | return semaphore.tryAcquire(); 115 | semaphore = new Semaphore(permits); 116 | lockMap.put(uniqueKey, semaphore); 117 | return semaphore.tryAcquire(); 118 | } 119 | } 120 | 121 | @Override 122 | public boolean releaseLock(String uniqueKey) { 123 | Semaphore semaphore = lockMap.get(uniqueKey); 124 | if (null != semaphore) 125 | semaphore.release(); 126 | return true; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/base/AbstractBaseFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.base; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import com.cpthack.commons.rdclient.config.RedisConfig; 22 | import com.github.cpthack.commons.ratelimiter.config.RateLimiterConfig; 23 | import com.github.cpthack.commons.ratelimiter.constants.RateLimiterConstants; 24 | import com.github.cpthack.commons.ratelimiter.utils.ProxyHelper; 25 | 26 | /** 27 | * AbstractBaseFactory.java
28 | * 29 | *
 30 |  * 抽象基础工厂类
 31 |  * 
32 | * 33 | * @author cpthack cpt@jianzhimao.com 34 | * @date May 24, 2017 12:32:49 PM 35 | * @since JDK 1.7 36 | */ 37 | public abstract class AbstractBaseFactory { 38 | /** 39 | * 单机实现 实例集合 40 | */ 41 | private static Map singleMap = new HashMap(); 42 | 43 | /** 44 | * 分布式实现 实例集合 45 | */ 46 | private static Map distributedMap = new HashMap(); 47 | 48 | private Class singleClass; 49 | private Class distributedClass; 50 | 51 | @SuppressWarnings("unchecked") 52 | public AbstractBaseFactory(Class singleClass, Class distributedClass) { 53 | this.singleClass = (Class) singleClass; 54 | this.distributedClass = (Class) distributedClass; 55 | } 56 | 57 | @SuppressWarnings("hiding") 58 | public T single() { 59 | return single(null); 60 | } 61 | 62 | @SuppressWarnings({ "unchecked", "hiding" }) 63 | public T single(RateLimiterConfig rateLimiterConfig) { 64 | T limiter = null; 65 | if (null == rateLimiterConfig) {// 如果配置变量为空,则启用默认配置,默认配置需要依赖:rate-limiter.properties文件 66 | String rateLimiterDefaultConfigName = RateLimiterConstants.RATE_LIMITER_CONFIG_FILE; 67 | limiter = (T) singleMap.get(rateLimiterDefaultConfigName); 68 | if (null != limiter) { 69 | return limiter; 70 | } 71 | limiter = (T) ProxyHelper.getInstance(singleClass); 72 | 73 | singleMap.put(rateLimiterDefaultConfigName, limiter); 74 | return limiter; 75 | } 76 | 77 | limiter = (T) singleMap.get(rateLimiterConfig.getConfigFile()); 78 | if (null != limiter) { 79 | return limiter; 80 | } 81 | limiter = (T) ProxyHelper.getInstance(singleClass, new Class[] { RateLimiterConfig.class }, new Object[] { rateLimiterConfig }); 82 | singleMap.put(rateLimiterConfig.getConfigFile(), limiter); 83 | return limiter; 84 | } 85 | 86 | @SuppressWarnings("hiding") 87 | public T distributed() { 88 | return distributed(null); 89 | } 90 | 91 | @SuppressWarnings("hiding") 92 | public T distributed(RateLimiterConfig rateLimiterConfig) { 93 | return distributed(rateLimiterConfig, null); 94 | } 95 | 96 | @SuppressWarnings({ "unchecked", "hiding" }) 97 | public T distributed(RateLimiterConfig rateLimiterConfig, RedisConfig redisConfig) { 98 | T limiter = null; 99 | if (null == rateLimiterConfig) {// 如果配置变量为空,则启用默认配置,默认配置需要依赖:rate-limiter.properties文件 100 | String rateLimiterDefaultConfigName = RateLimiterConstants.RATE_LIMITER_CONFIG_FILE; 101 | limiter = (T) distributedMap.get(rateLimiterDefaultConfigName); 102 | if (null != limiter) { 103 | return limiter; 104 | } 105 | limiter = (T) ProxyHelper.getInstance(distributedClass); 106 | distributedMap.put(rateLimiterDefaultConfigName, limiter); 107 | return limiter; 108 | } 109 | 110 | limiter = (T) distributedMap.get(rateLimiterConfig.getConfigFile()); 111 | if (null != limiter) 112 | return limiter; 113 | limiter = (T) ProxyHelper.getInstance(distributedClass, new Class[] { RateLimiterConfig.class, RedisConfig.class }, new Object[] { rateLimiterConfig, redisConfig }); 114 | distributedMap.put(rateLimiterConfig.getConfigFile(), limiter); 115 | return limiter; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/limiter/SingleLimiter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.limiter; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.concurrent.ConcurrentHashMap; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import com.github.cpthack.commons.ratelimiter.bean.LimiterBean; 26 | import com.github.cpthack.commons.ratelimiter.config.RateLimiterConfig; 27 | import com.google.common.util.concurrent.RateLimiter; 28 | 29 | /** 30 | * SingleLimiter.java
31 | * 32 | *
 33 |  * 限流接口 - 单机实现
 34 |  * 
35 | * 36 | * @author cpthack cpt@jianzhimao.com 37 | * @date May 16, 2017 6:04:29 PM 38 | * @since JDK 1.7 39 | */ 40 | public class SingleLimiter implements Limiter { 41 | 42 | private final static Logger logger = LoggerFactory.getLogger(SingleLimiter.class); 43 | 44 | private static Map rateLimiterMap = null; 45 | 46 | public SingleLimiter() { 47 | this(null); 48 | } 49 | 50 | public SingleLimiter(RateLimiterConfig rateLimiterConfig) { 51 | if (null == rateLimiterConfig) { 52 | rateLimiterConfig = new RateLimiterConfig(); 53 | } 54 | initRateLimiterMap(rateLimiterConfig); 55 | } 56 | 57 | /** 58 | * 59 | * initRateLimiterMap
60 | *
61 | * 62 | * 初始化限流配置
63 | * 初始化的限流配置在存储在Map中,支持后期动态添加。动态添加路由配置请参考 [method:dynamicAddRouter] 64 | * 65 | * @author cpthack cpt@jianzhimao.com 66 | * @param rateLimiterConfig 67 | * 限流配置 68 | * 69 | */ 70 | protected void initRateLimiterMap(RateLimiterConfig rateLimiterConfig) { 71 | if (null != rateLimiterMap) 72 | return; 73 | List limiterList = rateLimiterConfig.getLimiterList(); 74 | rateLimiterMap = new ConcurrentHashMap(); 75 | for (LimiterBean limiterBean : limiterList) { 76 | rateLimiterMap.put(limiterBean.getRouter(), RateLimiter.create(limiterBean.getCount() * 1.0 / limiterBean.getTime())); 77 | logger.debug("单机限流-加载限流配置>>>router = [{}],time = [{}],count = [{}]", limiterBean.getRouter(), limiterBean.getTime(), limiterBean.getCount()); 78 | } 79 | } 80 | 81 | @Override 82 | public boolean execute(String routerName) { 83 | RateLimiter rateLimiter = rateLimiterMap.get(routerName); 84 | if (null == rateLimiter) { 85 | return true; 86 | } 87 | return rateLimiter.tryAcquire(1); 88 | } 89 | 90 | @Override 91 | public boolean execute(String routerName, int limitCount) { 92 | return execute(routerName, limitCount, Integer.MAX_VALUE); 93 | } 94 | 95 | @Override 96 | public boolean execute(String routerName, int limitCount, int time) { 97 | RateLimiter rateLimiter = rateLimiterMap.get(routerName); 98 | if (null != rateLimiter) { 99 | return rateLimiter.tryAcquire();// 如果限流配置已经存在,则直接进行锁许可证申请 100 | } 101 | 102 | boolean isGetPermit = dynamicAddRouter(routerName, limitCount, time); 103 | return isGetPermit; 104 | } 105 | 106 | /** 107 | * dynamicAddRouter
108 | *
109 | * 当限流配置不存在的时候,需要进行动态限流配置。
110 | * 当多个线程同时进行动态配置时会发生并发问题,所以需要利用常量池特性[ routerName.intern() ]进行仅同一路由加锁。 111 | * 112 | * @author cpthack cpt@jianzhimao.com 113 | * @param routerName 114 | * 路由名称 115 | * @param limitCount 116 | * 限流数量 117 | * @param time 118 | * 限流时间,单位是秒 119 | * @return boolean 120 | * 121 | */ 122 | public boolean dynamicAddRouter(String routerName, int limitCount, int time) { 123 | synchronized (routerName.intern()) { 124 | RateLimiter rateLimiter = rateLimiterMap.get(routerName); 125 | if (rateLimiter == null) { 126 | rateLimiter = RateLimiter.create(limitCount * 1.0 / time); 127 | rateLimiterMap.put(routerName, rateLimiter); 128 | logger.info("单机限流-动态限流配置>>>router = [{}],time = [{}],count = [{}]", routerName, time, limitCount); 129 | } 130 | else { 131 | logger.warn("(重复添加限流配置)>>>router = [{}],time = [{}],count = [{}]", routerName, time, limitCount); 132 | } 133 | return rateLimiter.tryAcquire(); 134 | } 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/limiter/DistributedLimiter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.limiter; 17 | 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import com.cpthack.commons.rdclient.config.RedisConfig; 26 | import com.cpthack.commons.rdclient.core.RedisClient; 27 | import com.cpthack.commons.rdclient.core.RedisClientFactory; 28 | import com.github.cpthack.commons.ratelimiter.bean.LimiterBean; 29 | import com.github.cpthack.commons.ratelimiter.config.RateLimiterConfig; 30 | 31 | /** 32 | * DistributedLimiter.java
33 | * 34 | *
 35 |  * 限流接口 - 分布式实现
 36 |  * 
37 | * 38 | * @author cpthack cpt@jianzhimao.com 39 | * @date May 16, 2017 6:03:13 PM 40 | * @since JDK 1.7 41 | */ 42 | public class DistributedLimiter implements Limiter { 43 | 44 | private final static Logger logger = LoggerFactory.getLogger(DistributedLimiter.class); 45 | 46 | @SuppressWarnings("rawtypes") 47 | private static RedisClient redisClient = null; 48 | private static Map limiterBeanMap = null; 49 | 50 | public DistributedLimiter() { 51 | this(null, null); 52 | } 53 | 54 | public DistributedLimiter(RateLimiterConfig rateLimiterConfig) { 55 | this(rateLimiterConfig, null); 56 | } 57 | 58 | public DistributedLimiter(RateLimiterConfig rateLimiterConfig, RedisConfig redisConfig) { 59 | if (null == rateLimiterConfig) { 60 | rateLimiterConfig = new RateLimiterConfig(); 61 | } 62 | initLimiterConfig(rateLimiterConfig, redisConfig); 63 | } 64 | 65 | /** 66 | * 67 | * initLimiterConfig
68 | *
69 | * 70 | * 初始化限流配置
71 | * 72 | * @author cpthack cpt@jianzhimao.com 73 | * @param rateLimiterConfig 74 | * 限流配置类 75 | * @param redisConfig 76 | * 缓存配置类 77 | * 78 | */ 79 | protected void initLimiterConfig(RateLimiterConfig rateLimiterConfig, RedisConfig redisConfig) { 80 | if (null != redisClient) // 当redisClient不为空,意味着限流配置已经初始化到缓存中 81 | return; 82 | 83 | redisClient = RedisClientFactory.getClient(redisConfig); 84 | limiterBeanMap = new HashMap(); 85 | 86 | List limiterList = rateLimiterConfig.getLimiterList(); 87 | for (LimiterBean limiterBean : limiterList) { 88 | redisClient.setnx(limiterBean.getRouter(), "0", limiterBean.getTime()); 89 | limiterBeanMap.put(limiterBean.getRouter(), limiterBean); 90 | logger.debug("分布式限流-加载限流配置>>>router = [{}],time = [{}],count = [{}]", limiterBean.getRouter(), limiterBean.getTime(), limiterBean.getCount()); 91 | } 92 | } 93 | 94 | @Override 95 | public boolean execute(String routerName) { 96 | LimiterBean limiterBean = limiterBeanMap.get(routerName); 97 | if (null == limiterBean)// 表示没有相关限流配置,直接返回成功 98 | return true; 99 | int limiterCount = limiterBean.getCount(); 100 | 101 | /** 102 | * 每次根据路由地址强制设置缓存和过期时间,防止缓存过期后导致限流失效
103 | * 倘若已经存在该路由的缓存KEY,不会设置新值 104 | */ 105 | redisClient.setnx(limiterBean.getRouter(), "0", limiterBean.getTime()); 106 | long currentCount = redisClient.incr(routerName); 107 | 108 | if (currentCount > limiterCount)// 如果超过限流值,则直接返回false 109 | return false; 110 | 111 | return true; 112 | } 113 | 114 | @Override 115 | public boolean execute(String routerName, int limitCount) { 116 | return execute(routerName, limitCount, Integer.MAX_VALUE); 117 | } 118 | 119 | @Override 120 | public boolean execute(String routerName, int limitCount, int time) { 121 | LimiterBean limiterBean = limiterBeanMap.get(routerName); 122 | /** 123 | * 此处无需担心并发问题,当多个线程同时进行该模块代码,不影响限流配置。
124 | * limiterBeanMap对象中只会存在一条 [KEY = routerName] 的数据。 125 | */ 126 | if (null == limiterBean) { 127 | limiterBean = new LimiterBean(); 128 | limiterBean.setRouter(routerName); 129 | limiterBean.setCount(limitCount); 130 | limiterBean.setTime(time); 131 | limiterBeanMap.put(routerName, limiterBean); 132 | logger.debug("分布式限流-动态限流配置>>>router = [{}],time = [{}],count = [{}]", routerName, time, limitCount); 133 | } 134 | return execute(routerName); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/config/RateLimiterConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.config; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import com.github.cpthack.commons.ratelimiter.bean.LimiterBean; 22 | import com.github.cpthack.commons.ratelimiter.bean.LockBean; 23 | import com.github.cpthack.commons.ratelimiter.constants.RateLimiterConstants; 24 | 25 | /** 26 | * RateLimiterConfig.java
27 | * 28 | *
 29 |  * 默认的配置类
 30 |  * 
31 | * 32 | * @author cpthack cpt@jianzhimao.com 33 | * @date May 16, 2017 5:01:26 PM 34 | * @since JDK 1.7 35 | */ 36 | public class RateLimiterConfig extends AbstractConfig { 37 | 38 | private final String FILE_NAME = RateLimiterConstants.RATE_LIMITER_CONFIG_FILE; 39 | 40 | private final String MAX_LOCK_TASK_COUNT_NAME = "rate.limiter.lock.task.count"; 41 | private final String LOCK_TASK_ROURTE_NAME_PRE = "rate.limiter.lock.router."; 42 | private final String LOCK_TASK_EXPIRE_TIME_PRE = "rate.limiter.lock.exprie.time."; 43 | private final String LOCK_TASK_PERMITS_COUNT_PRE = "rate.limiter.lock.permits.count."; 44 | 45 | private final String MAX_LIMITER_TASK_COUNT_NAME = "rate.limiter.limiter.task.count"; 46 | private final String LIMITER_TASK_ROUTER_NAME_PRE = "rate.limiter.limiter.router."; 47 | private final String LIMITER_TASK_TIME_NAME_PRE = "rate.limiter.limiter.time."; 48 | private final String LIMITER_TASK_COUNT_NAME_PRE = "rate.limiter.limiter.count."; 49 | 50 | public RateLimiterConfig() { 51 | reloadConfig(); 52 | } 53 | 54 | @Override 55 | public String getConfigFile() { 56 | return FILE_NAME; 57 | } 58 | 59 | /** 60 | * 61 | * getLockList
62 | *
63 | * 64 | * 从本地配置文件中加载并发配置列表,并组装成List对象
65 | * 66 | * @author cpthack cpt@jianzhimao.com 67 | * @return List 68 | * 69 | */ 70 | public List getLockList() { 71 | 72 | int defaultLockListSize = getPropertyToInt(MAX_LOCK_TASK_COUNT_NAME, RateLimiterConstants.MAX_RATE_LIMITER_LOCK_LIMIT); 73 | List lockList = null; 74 | LockBean lockBean = null; 75 | String lockUniqueKey = null; 76 | int expireTime; 77 | int permitsCount; 78 | for (int i = 0; i < defaultLockListSize; i++) { 79 | lockUniqueKey = getProperty(LOCK_TASK_ROURTE_NAME_PRE + (i + 1)); 80 | if (null == lockUniqueKey) 81 | continue; 82 | 83 | lockBean = new LockBean(); 84 | lockBean.setUniqueKey(lockUniqueKey); 85 | 86 | expireTime = getPropertyToInt(LOCK_TASK_EXPIRE_TIME_PRE + (i + 1), RateLimiterConstants.LOCK_DEFAULT_EXPIRE_TIME); 87 | lockBean.setExpireTime(expireTime); 88 | 89 | permitsCount = getPropertyToInt(LOCK_TASK_PERMITS_COUNT_PRE + (i + 1), RateLimiterConstants.LOCK_DEFAULT_PERMITS_NUM); 90 | lockBean.setPermits(permitsCount); 91 | 92 | if (null == lockList) 93 | lockList = new ArrayList(defaultLockListSize); 94 | lockList.add(lockBean); 95 | } 96 | return lockList; 97 | } 98 | 99 | /** 100 | * 101 | * getLimiterList
102 | *
103 | * 104 | * 从本地配置文件中加载限流配置列表,并组装成List对象返回
105 | * 106 | * @author cpthack cpt@jianzhimao.com 107 | * @return List 108 | * 109 | */ 110 | public List getLimiterList() { 111 | 112 | int defaultLimiterListSize = getPropertyToInt(MAX_LIMITER_TASK_COUNT_NAME, RateLimiterConstants.MAX_RATE_LIMITER_LIMITER_LIMIT); 113 | List limiterList = null; 114 | LimiterBean limiterBean = null; 115 | String limiterRouter = null; 116 | int limiterTime; 117 | int limiterCount; 118 | for (int i = 0; i < defaultLimiterListSize; i++) { 119 | limiterRouter = getProperty(LIMITER_TASK_ROUTER_NAME_PRE + (i + 1)); 120 | if (null == limiterRouter) 121 | continue; 122 | limiterBean = new LimiterBean(); 123 | limiterBean.setRouter(limiterRouter); 124 | 125 | limiterTime = getPropertyToInt(LIMITER_TASK_TIME_NAME_PRE + (i + 1), Integer.MAX_VALUE); 126 | limiterBean.setTime(limiterTime); 127 | 128 | limiterCount = getPropertyToInt(LIMITER_TASK_COUNT_NAME_PRE + (i + 1), 1); 129 | limiterBean.setCount(limiterCount); 130 | 131 | if (null == limiterList) 132 | limiterList = new ArrayList(defaultLimiterListSize); 133 | limiterList.add(limiterBean); 134 | } 135 | return limiterList; 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/lock/DistributedLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-2020, cpthack 成佩涛 (cpt@jianzhimao.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.github.cpthack.commons.ratelimiter.lock; 17 | 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import com.cpthack.commons.rdclient.config.RedisConfig; 26 | import com.cpthack.commons.rdclient.core.RedisClient; 27 | import com.cpthack.commons.rdclient.core.RedisClientFactory; 28 | import com.github.cpthack.commons.ratelimiter.bean.LockBean; 29 | import com.github.cpthack.commons.ratelimiter.config.RateLimiterConfig; 30 | import com.github.cpthack.commons.ratelimiter.constants.RateLimiterConstants; 31 | 32 | /** 33 | * DistributedLock.java
34 | * 35 | *
 36 |  * 分布式锁 实现类
 37 |  * 
38 | * 39 | * @author cpthack cpt@jianzhimao.com 40 | * @date May 22, 2017 12:41:02 PM 41 | * @since JDK 1.7 42 | */ 43 | public class DistributedLock implements Lock { 44 | 45 | private final static Logger logger = LoggerFactory.getLogger(DistributedLock.class); 46 | 47 | private static RedisClient redisClient = null; 48 | private static Map lockBeanMap = null; 49 | 50 | public DistributedLock() { 51 | this(null, null); 52 | } 53 | 54 | public DistributedLock(RateLimiterConfig rateLimiterConfig, RedisConfig redisConfig) { 55 | if (null == rateLimiterConfig) 56 | rateLimiterConfig = new RateLimiterConfig(); 57 | initLockConfig(rateLimiterConfig, redisConfig); 58 | } 59 | 60 | /** 61 | * 62 | * initLockConfig
63 | *
64 | * 65 | * 初始化并发锁配置
66 | * 67 | * @author cpthack cpt@jianzhimao.com 68 | * @param rateLimiterConfig 69 | * @param redisConfig 70 | * void 71 | * 72 | */ 73 | protected void initLockConfig(RateLimiterConfig rateLimiterConfig, RedisConfig redisConfig) { 74 | if (null != redisClient) // 当redisClient不为空,意味着限流配置已经初始化到缓存中 75 | return; 76 | redisClient = RedisClientFactory.getClient(redisConfig); 77 | lockBeanMap = new HashMap(); 78 | 79 | List lockList = rateLimiterConfig.getLockList(); 80 | for (LockBean lockBean : lockList) { 81 | logger.debug("分布式并发锁-加载并发配置>>>uniqueKey = [{}],time = [{}],count = [{}]", lockBean.getUniqueKey(), lockBean.getExpireTime(), lockBean.getPermits()); 82 | redisClient.setnx(lockBean.getUniqueKey(), "0", lockBean.getExpireTime()); 83 | lockBeanMap.put(lockBean.getUniqueKey(), lockBean); 84 | } 85 | } 86 | 87 | @Override 88 | public boolean lock(String uniqueKey) { 89 | return lock(uniqueKey, RateLimiterConstants.LOCK_DEFAULT_EXPIRE_TIME); 90 | } 91 | 92 | @Override 93 | public boolean lock(String uniqueKey, int expireTime) { 94 | return lock(uniqueKey, expireTime, RateLimiterConstants.LOCK_DEFAULT_PERMITS_NUM); 95 | } 96 | 97 | private LockBean getLockBean(String uniqueKey, int expireTime, int permits) { 98 | LockBean lockBean = lockBeanMap.get(uniqueKey); 99 | if (null == lockBean) { 100 | lockBean = new LockBean(); 101 | lockBean.setUniqueKey(uniqueKey); 102 | lockBean.setExpireTime(expireTime); 103 | lockBean.setPermits(permits); 104 | lockBeanMap.put(uniqueKey, lockBean); 105 | } 106 | return lockBean; 107 | } 108 | 109 | @Override 110 | public boolean lock(String uniqueKey, int expireTime, int permits) { 111 | LockBean lockBean = getLockBean(uniqueKey, expireTime, permits); 112 | expireTime = lockBean.getExpireTime(); 113 | permits = lockBean.getPermits(); 114 | long result = redisClient.setnx(uniqueKey, "0", lockBean.getExpireTime()); 115 | result = redisClient.incr(uniqueKey); 116 | 117 | // if (result <= 0) {// 应对因当releaseLock操作执行多次等问题而导致缓存中驻留着value小于0的数据 118 | // logger.warn("并发锁,检测到当前KEY = [{}] ,VALUE = [{}] ,强制执行清除命令,便于后续请求顺利进行.", uniqueKey, 119 | // result); 120 | // return lock(uniqueKey, expireTime, permits); 121 | // } 122 | 123 | if (result > permits) { 124 | redisClient.decr(uniqueKey); 125 | logger.warn("并发锁,检测到当前KEY = [{}] , VALUE = [{}] , permits = [{}],超过许可范围,因而获取锁失败.", uniqueKey, result, permits); 126 | return false; 127 | } 128 | 129 | if (result > 0 && result <= permits) { 130 | logger.info("并发锁,检测到当前KEY = [{}] , VALUE = [{}] , permits = [{}],满足许可范围,因为成功获得锁.", uniqueKey, result, permits); 131 | return true; 132 | } 133 | 134 | logger.debug("并发锁,检测到当前KEY = [{}] , VALUE = [{}] , permits = [{}],异常逻辑导致获得锁失败.", uniqueKey, result, permits); 135 | return false; 136 | } 137 | 138 | @Override 139 | public boolean releaseLock(String uniqueKey) { 140 | LockBean lockBean = lockBeanMap.get(uniqueKey); 141 | if (null == lockBean) { 142 | // TODO 抛出异常 143 | return false; 144 | } 145 | long result = redisClient.setnx(uniqueKey, "0", lockBean.getExpireTime()); 146 | if (result == 0) { 147 | redisClient.decr(uniqueKey); 148 | } 149 | return true; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-rate-limiter 2 | 3 | 基于Guava的RateLimiter、Redis封装了并发控制、限流管理功能,支持配置文件和硬编码形式进行限流控制,控制粒度在方法级别,使用简单。 4 | 5 | - **限流控制**:主要用于服务的请求频率限制,避免由于服务吞吐量跟不上造成上层调用请求堆积过大而导致服务垮掉甚至应用奔溃问题。目前提供了单机版限流和分布式限流功能。简而言之,通过限流控制,我们可以在方法级别的粒度控制到在XXX秒内最多接收XXX次调用。 6 | 7 | - **并发控制**:与限流控制类似,并发控制更加强调的是并发量。该功能能够保证服务在任何时刻的并发量控制。目前提供了单机和分布式两个版本的并发控制。简而言之,通过并发控制,我们可以在方法级别的粒度控制到某个服务同时间最多不能超过XXX次请求被调用。 8 | 9 | ## 一、使用示例: 10 | 11 | #### 1、自定义RateLimiterConfig配置 12 | > CustomRateLimiterConfig代码引用 13 | 14 | public class CustomRateLimiterConfig extends RateLimiterConfig { 15 | 16 | private final String FILE_NAME = "test-rate-limiter.properties"; 17 | 18 | @Override 19 | public String getConfigFile() { 20 | return FILE_NAME; 21 | } 22 | 23 | } 24 | 继承RateLimiterConfig类并且重写getConfigFile方法重新制定配置文件名称即可. 25 | 26 | 配置文件模板如:[test-rate-limiter.properties](https://github.com/cpthack/java-rate-limiter/blob/master/src/test/resources/test-rate-limiter.properties)所示,主要参考配置项 27 | 28 | #### 2、自定义RedisConfig配置 29 | > RateRedisConfig代码引用 30 | 31 | public class RateRedisConfig extends RedisConfig { 32 | 33 | private final String FILE_NAME = "test-redis_config.properties"; 34 | 35 | @Override 36 | public String getConfigFile() { 37 | return FILE_NAME; 38 | } 39 | } 40 | 继承RedisConfig类并且重写getConfigFile方法重新制定配置文件名称即可. 41 | 42 | 配置文件模板如:[test-redis_config.properties](https://github.com/cpthack/java-rate-limiter/blob/master/src/test/resources/test-redis_config.properties)所示,主要参考配置项 43 | 44 | #### 3、限流控制使用 45 | > LimiterTest.java代码引用 46 | 47 | private final static Logger logger = LoggerFactory.getLogger(LimiterTest.class); 48 | private static Limiter limiter = null; 49 | private volatile static int maxNum = 0; 50 | private final static String ROUTER_NAME = "/thread-test"; 51 | 52 | public static void main(String[] args) { 53 | RedisConfig redisConfig = new RateRedisConfig(); 54 | RateLimiterConfig rateLimiterConfig = new CustomRateLimiterConfig(); 55 | 56 | singleLimiter(rateLimiterConfig);// 实例化单机限流对象 57 | 58 | // DistributedLimiter(rateLimiterConfig, redisConfig); // 示例化分布式限流对象 59 | 60 | simulateConcurrentThread(); // 模拟并发线程 61 | } 62 | 63 | private static void simulateConcurrentThread() { 64 | DoThing dt = null; 65 | Thread t = null; 66 | for (int i = 0; i < 6; i++) { 67 | dt = new DoThing("Thread " + i); 68 | t = new Thread(dt); 69 | t.start(); 70 | } 71 | } 72 | 73 | private static void singleLimiter(RateLimiterConfig rateLimiterConfig) { 74 | limiter = LimiterFactory.getInstance().single(rateLimiterConfig); 75 | } 76 | 77 | private static void DistributedLimiter(RateLimiterConfig rateLimiterConfig, RedisConfig redisConfig) { 78 | limiter = LimiterFactory.getInstance().distributed(rateLimiterConfig, redisConfig); 79 | } 80 | 81 | /** 82 | * 自定义线程,用于模拟并发 83 | */ 84 | static class DoThing implements Runnable { 85 | String name; 86 | 87 | public DoThing(String name) { 88 | this.name = name; 89 | } 90 | 91 | @SuppressWarnings("static-access") 92 | @Override 93 | public void run() { 94 | try { 95 | for (int i = 0; i < 20; i++) { 96 | 97 | if (!limiter.execute(ROUTER_NAME, 4, 1)) {// 进行限流控制 98 | 99 | logger.info("Thread Name is [{}],调用频率太高了.", name); 100 | Thread.currentThread().sleep(1000); 101 | continue; 102 | } 103 | maxNum++; 104 | logger.info("Thread Name is [{}],最新maxNum的值 = [" + maxNum + "]", name); 105 | } 106 | } 107 | catch (InterruptedException e) { 108 | logger.error("Thread Name [{}] is Error.", name, e); 109 | } 110 | } 111 | } 112 | 113 | #### 4、并发控制 114 | > LimiterTest.java代码引用 115 | 116 | private final static Logger logger = LoggerFactory.getLogger(LockTest.class); 117 | 118 | private static String UNIQUE_KEY = "/lock1"; 119 | private static Lock lock = null; 120 | 121 | private volatile static int successNum = 0; 122 | 123 | public static void main(String[] args) { 124 | RateLimiterConfig rateLimiterConfig = new CustomRateLimiterConfig(); 125 | // lock = getSingleLock(rateLimiterConfig); 126 | RedisConfig redisConfig = new RateRedisConfig(); 127 | 128 | lock = getDistributedLock(rateLimiterConfig, redisConfig); 129 | 130 | // RedisClientFactory.getClient(redisConfig).set("/lock1", "10");// 131 | // 模拟releaseLock没有执行导致的缓存中存在较多正数值得锁KEY 132 | 133 | simulateConcurrentThread(80); // 模拟并发线程 134 | } 135 | 136 | private static Lock getSingleLock(RateLimiterConfig rateLimiterConfig) { 137 | return LockFactory.getInstance().single(rateLimiterConfig); 138 | } 139 | 140 | private static Lock getDistributedLock(RateLimiterConfig rateLimiterConfig, RedisConfig redisConfig) { 141 | return LockFactory.getInstance().distributed(rateLimiterConfig, redisConfig); 142 | } 143 | 144 | private static void simulateConcurrentThread(int threadNum) { 145 | DoThing dt = null; 146 | Thread t = null; 147 | for (int i = 0; i < threadNum; i++) { 148 | dt = new DoThing("Thread " + i); 149 | t = new Thread(dt); 150 | try { 151 | Thread.sleep(100); 152 | } 153 | catch (InterruptedException e) { 154 | e.printStackTrace(); 155 | }// 模拟程序执行时间 156 | t.start(); 157 | } 158 | } 159 | 160 | /** 161 | * 自定义线程,用于模拟并发 162 | */ 163 | static class DoThing implements Runnable { 164 | String name; 165 | 166 | public DoThing(String name) { 167 | this.name = name; 168 | } 169 | 170 | @SuppressWarnings("static-access") 171 | @Override 172 | public void run() { 173 | try { 174 | if (lock.lock(UNIQUE_KEY)) {// 进行并发控制 175 | 176 | logger.info("Thread Name is [{}] 成功获得锁,正在处理中...", name); 177 | 178 | successNum++; 179 | logger.info("当前成功并发数successNum的值为 [" + successNum + "]"); 180 | Thread.currentThread().sleep(2000);// 模拟程序执行时间 181 | 182 | successNum--; 183 | lock.releaseLock(UNIQUE_KEY); 184 | } 185 | else { 186 | logger.warn("Thread Name is [{}] 尝试获得锁失败", name); 187 | } 188 | } 189 | catch (InterruptedException e) { 190 | logger.error("Thread Name [{}] is Error.", name, e); 191 | } 192 | } 193 | } 194 | 195 | ## 二、具体参考: 196 | 197 | * 依赖redis-client项目:[redis-client](https://github.com/cpthack/redis-client) 198 | 199 | * 限流控制示例参考类:[LimiterTest.java](https://github.com/cpthack/java-rate-limiter/blob/master/src/test/java/com/github/cpthack/commons/ratelimiter/limiter/LimiterTest.java) 200 | 201 | * 并发控制示例参考类:[LockTest.java](https://github.com/cpthack/java-rate-limiter/blob/master/src/test/java/com/github/cpthack/commons/ratelimiter/lock/LockTest.java) -------------------------------------------------------------------------------- /src/main/java/com/github/cpthack/commons/ratelimiter/config/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.cpthack.commons.ratelimiter.config; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedReader; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.FileOutputStream; 9 | import java.io.FileReader; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.io.OutputStream; 14 | import java.io.UnsupportedEncodingException; 15 | import java.text.SimpleDateFormat; 16 | import java.util.ArrayList; 17 | import java.util.Date; 18 | import java.util.HashMap; 19 | import java.util.Iterator; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.Properties; 23 | import java.util.Set; 24 | 25 | import org.apache.commons.lang3.StringUtils; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | public final class FileUtils { 30 | private static Logger logger = LoggerFactory.getLogger(FileUtils.class); 31 | private static SimpleDateFormat sdfYMD = new SimpleDateFormat("yyyyMMdd/"); 32 | private static SimpleDateFormat sdfFile = new SimpleDateFormat( 33 | "yyyyMMddHHmmss"); 34 | 35 | public static boolean createSubFolder(String pathName) { 36 | boolean blReturn = false; 37 | if (pathName == null || pathName.trim().equals("")) { 38 | return blReturn; 39 | } 40 | if (pathName.indexOf("%20") >= 0) { 41 | pathName = pathName.replace("%20", " "); 42 | } 43 | pathName = pathName.replaceAll("\\\\", "/"); 44 | String[] dirs = pathName.split("/"); 45 | String tempDir = ""; 46 | for (int i = 0; i < dirs.length; i++) { 47 | tempDir = tempDir + "/" + dirs[i]; 48 | File file = new File(tempDir); 49 | if (!file.exists()) { 50 | file.mkdirs(); 51 | } 52 | } 53 | blReturn = true; 54 | return blReturn; 55 | } 56 | 57 | public static ClassLoader getClassLoader() { 58 | ClassLoader classLoader = Thread.currentThread() 59 | .getContextClassLoader(); 60 | if (classLoader == null) { 61 | classLoader = FileUtils.class.getClassLoader(); 62 | } 63 | return classLoader; 64 | } 65 | 66 | /** 67 | * 读取静态配置文件的属性(此配置文件仅load一次,适用于程序内不再修改的配置文件);如果返回null,则读取不成功! 68 | * 69 | * @param fileName 70 | * 文件名(classes目录下) 71 | * @return 72 | */ 73 | public static Properties loadStaticProperties(String fileName) { 74 | if (StringUtils.isBlank(fileName)) { 75 | return null; 76 | } 77 | logger.debug("loadStaticProperties()-- start,[" + fileName + "]"); 78 | Properties properties = null; 79 | try { 80 | ClassLoader classLoader = getClassLoader(); 81 | InputStream is = classLoader.getResourceAsStream(fileName); 82 | InputStreamReader isr = new InputStreamReader(is, "UTF-8"); 83 | 84 | Properties temp = new Properties(); 85 | temp.load(isr); 86 | properties = new Properties(); 87 | properties.putAll(temp); 88 | temp.clear(); 89 | temp = null; 90 | is.close(); 91 | is = null; 92 | isr.close(); 93 | isr = null; 94 | } catch (Exception e) { 95 | logger.error("loadStaticProperties()[" + fileName + "]:" + e); 96 | } 97 | return properties; 98 | } 99 | 100 | /** 101 | * 读取动态配置文件的属性(此配置文件可多次读取,适用于配置文件在程序中修改的情形);如果返回null,则读取不成功! 102 | * 103 | * @param fileName 104 | * 文件名(classpath路径内) 105 | * @return 106 | */ 107 | public static Properties loadVarProperties(String fileName) { 108 | logger.debug("loadVarProperties()-- start,[" + fileName + "]"); 109 | if (StringUtils.isBlank(fileName)) { 110 | return null; 111 | } 112 | Properties properties = null; 113 | try { 114 | String root = getConfigRoot(); 115 | String file = root + fileName; 116 | logger.debug("loadVarProperties()-- full path name=[" + file + "]"); 117 | InputStream is = new FileInputStream(file); 118 | InputStreamReader isr = new InputStreamReader(is, "UTF-8"); 119 | Properties temp = new Properties(); 120 | temp.load(isr); 121 | properties = new Properties(); 122 | properties.putAll(temp); 123 | temp.clear(); 124 | temp = null; 125 | is.close(); 126 | is = null; 127 | isr.close(); 128 | isr = null; 129 | } catch (Exception e) { 130 | logger.error("loadVarProperties()[" + fileName + "]:" + e); 131 | } 132 | return properties; 133 | } 134 | 135 | /** 136 | * 写配置文件 137 | * 138 | * @param properties 139 | * 新的配置属性 140 | * @param fileName 141 | * 文件名(classes目录下) 142 | * @return 143 | */ 144 | public static boolean writeConfig(Properties properties, String fileName) { 145 | if (properties == null || StringUtils.isBlank(fileName)) { 146 | return false; 147 | } 148 | try { 149 | String root = getConfigRoot(); 150 | String file = root + fileName; 151 | logger.debug("writeConfig() -- fileName=[" + file + "]"); 152 | OutputStream out = new FileOutputStream(file); 153 | properties.store(out, ""); 154 | out.close(); 155 | } catch (Exception e) { 156 | logger.error("writeConfig()[" + fileName + "]:" + e); 157 | return false; 158 | } 159 | return true; 160 | } 161 | 162 | // 取得当前WEB应用的根目录 163 | public static String getWebRoot() { 164 | String temp = getConfigRoot(); 165 | String s = "/WEB-INF/classes/"; 166 | int index = temp.indexOf(s); 167 | if (index != -1) { 168 | temp = temp.substring(0, index + 1);// 最后带/ 169 | } 170 | return temp; 171 | } 172 | 173 | private static String getConfigRoot() { 174 | String root = null; 175 | // root = FileUtils.class.getResource("/").getPath(); 176 | if (isFromJar()) { 177 | logger.debug("getConfigRoot() -- is from JAR file."); 178 | root = FileUtils.class.getProtectionDomain().getCodeSource() 179 | .getLocation().getPath(); 180 | if (root != null && root.indexOf("/lib/") != -1) { 181 | int index = root.indexOf("/lib/"); 182 | root = root.substring(0, index) + "/classes/"; 183 | } 184 | } else { 185 | // for web context 186 | try { 187 | root = FileUtils.class.getResource("").getPath(); 188 | logger.debug("getConfigRoot() -- first,root=[" + root + "]"); 189 | String s = "/WEB-INF/classes/"; 190 | int index = root.indexOf(s); 191 | if (index != -1) { 192 | root = root.substring(0, index) + s; 193 | } 194 | } catch (Exception e) { 195 | logger.warn("getConfigRoot() -- in error:" + e); 196 | } 197 | } 198 | 199 | root = FileUtils.decodeFilePath(root); 200 | return root; 201 | 202 | } 203 | 204 | private static boolean isFromJar() { 205 | boolean blReturn = false; 206 | String name = "" + FileUtils.class.getResource("FileUtils.class"); 207 | if (name != null && name.toLowerCase().indexOf(".jar!/") != -1) { 208 | blReturn = true; 209 | } 210 | return blReturn; 211 | } 212 | 213 | public static String txtFromFile(String fullFileName, String charsetName) { 214 | String txt = ""; 215 | try { 216 | File file = new File(fullFileName); 217 | InputStream is = new FileInputStream(file); 218 | BufferedReader bf = new BufferedReader(new InputStreamReader(is, 219 | charsetName)); 220 | String line = null; 221 | txt = ""; 222 | while ((line = bf.readLine()) != null) { 223 | txt += line; 224 | } 225 | bf.close(); 226 | file = null; 227 | } catch (Exception e) { 228 | logger.error("txtFromFile() -- " + e); 229 | } 230 | return txt; 231 | } 232 | 233 | /** 234 | * 读取配置文件的内容(此文件仅load一次,适用于程序内不再修改的文件);如果返回null,则读取不成功! 235 | * 236 | * @param fileName 237 | * 文件名(classes目录下) 238 | * @return 239 | */ 240 | public static String readConfigFile(String fileName) { 241 | byte[] arrByte = null; 242 | String strReturn = null; 243 | try { 244 | ClassLoader classLoader = getClassLoader(); 245 | InputStream is = classLoader.getResourceAsStream(fileName); 246 | arrByte = new byte[is.available()]; 247 | is.read(arrByte); 248 | is.close(); 249 | } catch (Exception e) { 250 | logger.error("readConfigFile() -- fileName=[" + fileName 251 | + "],exception:" + e); 252 | return strReturn; 253 | } 254 | if (arrByte != null) { 255 | try { 256 | strReturn = new String(arrByte, "UTF-8"); 257 | } catch (UnsupportedEncodingException e) { 258 | } 259 | } 260 | return strReturn; 261 | } 262 | 263 | /** 264 | * 把文件读出形成字符串 265 | * 266 | * @param fileName 267 | * 文件名 268 | * @return 文件内容 269 | */ 270 | public static String readFile(String fileName) { 271 | String strReturn = null; 272 | byte[] arrByte = FileUtils.fileToBytes(fileName); 273 | if (arrByte != null) { 274 | strReturn = new String(arrByte); 275 | } 276 | return strReturn; 277 | } 278 | 279 | /** 280 | * 把文件读出形成字节流数组 281 | * 282 | * @param fileName 283 | * 带路径的文件名 284 | * @return 返回生成的字节流数据,不存在文件或者出错则返回null 285 | */ 286 | public static byte[] fileToBytes(String fileName) { 287 | if (fileName == null) { 288 | return null; 289 | } 290 | File file = new File(fileName); 291 | if (!file.exists() || !file.isFile()) { 292 | return null; 293 | } 294 | byte[] arrByte = null; 295 | try { 296 | arrByte = new byte[(int) file.length()]; 297 | FileInputStream is = new FileInputStream(file); 298 | is.read(arrByte); 299 | is.close(); 300 | } catch (Exception e) { 301 | } 302 | return arrByte; 303 | } 304 | 305 | /** 306 | * 把字节流数组存为指定的文件名 307 | * 308 | * @param fileName 309 | * 要存的文件名(带路径) 310 | * @param arrByte 311 | * 字节流数组 312 | * @return 成功返回true,否则返回false 313 | */ 314 | public static boolean bytesToFile(String fileName, byte[] arrByte) { 315 | boolean blReturn = false; 316 | if (fileName == null || arrByte == null || arrByte.length == 0) { 317 | return blReturn; 318 | } 319 | File file = new File(fileName); 320 | try { 321 | FileOutputStream os = new FileOutputStream(file, false); 322 | os.write(arrByte); 323 | os.close(); 324 | blReturn = true; 325 | } catch (Exception e) { 326 | } 327 | return blReturn; 328 | } 329 | 330 | /** 331 | * @return 返回yyyyMMdd/结构的目录字符串 332 | * @param date 333 | * 指定的时间 334 | */ 335 | public static String dateDir(Date date) { 336 | return sdfYMD.format(date); 337 | } 338 | 339 | /** 340 | * 返回当前时间的字符串用作文件名 341 | * 342 | * @param date 343 | * 指定的时间 344 | * @return 返回字符串 345 | */ 346 | public static String randomFileString(Date date) { 347 | String strReturn = null; 348 | long times = System.currentTimeMillis(); 349 | strReturn = sdfFile.format(date); 350 | strReturn += ("_" + times); 351 | return strReturn; 352 | } 353 | 354 | /** 355 | * 检查目录是否存在,不存在的如果可能的话就创建此目录(可为多级目录) 356 | * 357 | * @param filePath 358 | * 目录名 359 | * @return 操作成功返回true(即最终是有此目录),否则返回false 360 | */ 361 | public static boolean ensureFileDirs(String filePath) { 362 | boolean blReturn = false; 363 | if (StringUtils.isBlank(filePath)) { 364 | return blReturn; 365 | } 366 | File file = new File(filePath); 367 | return ensureFileDirs(file); 368 | } 369 | 370 | public static boolean ensureFileDirs(File file) { 371 | boolean blReturn = false; 372 | if (file == null) { 373 | return blReturn; 374 | } 375 | try { 376 | if (file.exists() && file.isDirectory()) { 377 | // 378 | } else { 379 | file.mkdirs(); 380 | } 381 | blReturn = true; 382 | } catch (Exception e) { 383 | } 384 | return blReturn; 385 | } 386 | 387 | /** 388 | * 删除指定的文件 389 | * 390 | * @param file 391 | * 文件名(带路径) 392 | * @return 成功删除就返回true,否则返回false 393 | */ 394 | public static boolean deleteFile(String filePath) { 395 | boolean blReturn = false; 396 | if (filePath == null || filePath.trim().equals("")) { 397 | return blReturn; 398 | } 399 | try { 400 | File file = new File(filePath); 401 | if (file.exists() && !file.isDirectory()) { 402 | blReturn = file.delete(); 403 | } 404 | } catch (Exception e) { 405 | } 406 | return blReturn; 407 | } 408 | 409 | /** 410 | * 删除文件夹以及文件夹下的子目录与文件 411 | * 412 | * @param filePath 413 | */ 414 | public static boolean deleteAllFile(File file) { 415 | boolean blReturn = false; 416 | try { 417 | if (file.exists()) { 418 | if (file.isFile()) { 419 | file.delete(); 420 | } else if (file.isDirectory()) { 421 | File files[] = file.listFiles(); 422 | for (int i = 0; i < files.length; i++) { 423 | FileUtils.deleteAllFile(files[i]); 424 | } 425 | } 426 | if (file.exists()) { 427 | file.delete(); 428 | } 429 | } 430 | } catch (Exception e) { 431 | } 432 | return blReturn; 433 | } 434 | 435 | /** 436 | * 将磁盘上的文件夹中所有的txt文件读出来,并且把文件名和文件内容封装到一个map中 437 | * 438 | * @param sourceFolderPath 439 | * 文件夹的路径 440 | * @return Map<名称,内容> 441 | */ 442 | public static Map getTxtContent(String sourceFolderPath) { 443 | if (null == sourceFolderPath || "".equals(sourceFolderPath)) { 444 | return null; 445 | } 446 | Map values = new HashMap(); 447 | File sourceFile = new File(sourceFolderPath); 448 | File[] textFiles = null; 449 | try { 450 | if (null != sourceFile) { 451 | textFiles = sourceFile.listFiles(); 452 | } 453 | for (int i = 0; textFiles != null && i < textFiles.length; i++) { 454 | if (textFiles[i].isFile() 455 | && textFiles[i].getName().endsWith(".txt")) { 456 | String name = textFiles[i].getName(); 457 | String content = getContentFromFile(textFiles[i] 458 | .getCanonicalPath()); 459 | values.put(name, content); 460 | } 461 | } 462 | } catch (Exception e) { 463 | e.printStackTrace(); 464 | } 465 | return values; 466 | } 467 | 468 | /** 469 | * 将磁盘上的文件夹中所有的txt文件读出来,并且把文件名和文件内容封装到一个map中 470 | * 471 | * @param sourceFolderPath 472 | * 文件夹的路径 473 | * @return Map<名称,内容> 474 | */ 475 | public static List> getTxtContentForIndex( 476 | String sourceFolderPath) { 477 | if (null == sourceFolderPath || "".equals(sourceFolderPath)) { 478 | return null; 479 | } 480 | List> list = new ArrayList>(); 481 | Map map = null; 482 | File sourceFile = new File(sourceFolderPath); 483 | File[] textFiles = null; 484 | try { 485 | if (null != sourceFile) { 486 | textFiles = sourceFile.listFiles(); 487 | } 488 | for (int i = 0; textFiles != null && i < textFiles.length; i++) { 489 | if (textFiles[i].isFile() 490 | && textFiles[i].getName().endsWith(".txt")) { 491 | String name = textFiles[i].getName(); 492 | String content = getContentFromFile(textFiles[i] 493 | .getCanonicalPath()); 494 | // System.out.println(name+"&&&&&&&&"+content); 495 | map = new HashMap(); 496 | map.put("name", name); 497 | map.put("content", content); 498 | } 499 | list.add(map); 500 | map = null; 501 | } 502 | } catch (Exception e) { 503 | e.printStackTrace(); 504 | } 505 | return list; 506 | } 507 | 508 | /** 509 | * 将磁盘中的文件读入内存中 510 | * 511 | * @param sourceFilePath 512 | * @return 513 | */ 514 | public static String getContentFromFile(String sourceFilePath) { 515 | String result = ""; 516 | File inputFile = null; 517 | FileReader fr = null; 518 | BufferedReader br = null; 519 | try { 520 | inputFile = new File(sourceFilePath); 521 | if (null != inputFile && inputFile.exists()) { 522 | fr = new FileReader(inputFile); 523 | // br = new BufferedReader(fr); 524 | br = new BufferedReader(new InputStreamReader( 525 | new FileInputStream(sourceFilePath), "GBK")); 526 | String tempString = ""; 527 | while ((tempString = br.readLine()) != null) { 528 | result += tempString + "\r\n"; 529 | } 530 | } 531 | } catch (Exception e) { 532 | e.printStackTrace(); 533 | } finally { 534 | try { 535 | if (null != br) { 536 | br.close(); 537 | } 538 | if (null != fr) { 539 | fr.close(); 540 | } 541 | } catch (Exception e2) { 542 | e2.printStackTrace(); 543 | } 544 | } 545 | return result; 546 | } 547 | 548 | public static String[] getMapKey(Map map) { 549 | String[] getFieldName = null; 550 | try { 551 | Set set = map.keySet(); 552 | Iterator iter = set.iterator(); 553 | List fields = new ArrayList(); 554 | while (iter.hasNext()) { 555 | fields.add(iter.next()); 556 | } 557 | getFieldName = new String[fields.size()]; 558 | for (int i = 0; i < fields.size(); i++) { 559 | getFieldName[i] = fields.get(i); 560 | } 561 | } catch (Exception e) { 562 | e.printStackTrace(); 563 | } 564 | return getFieldName; 565 | } 566 | 567 | public static String decodeFilePath(String root) { 568 | if (root != null && root.toLowerCase().indexOf("http:") == -1 569 | && root.indexOf("%20") != -1) { 570 | root = root.replace("%20", " "); 571 | } 572 | return root; 573 | } 574 | 575 | // 判断文件的编码格式 576 | public static String getFileCharset(File file) { 577 | String charset = null; 578 | FileInputStream fis = null; 579 | BufferedInputStream bin = null; 580 | try { 581 | fis = new FileInputStream(file); 582 | bin = new BufferedInputStream(fis); 583 | int p = (bin.read() << 8) + bin.read(); 584 | 585 | switch (p) { 586 | case 0xefbb: 587 | charset = "UTF-8"; 588 | break; 589 | case 0xfffe: 590 | charset = "Unicode"; 591 | break; 592 | case 0xfeff: 593 | charset = "UTF-16BE"; 594 | break; 595 | default: 596 | charset = "GBK"; 597 | } 598 | bin.close(); 599 | fis.close(); 600 | } catch (FileNotFoundException e) { 601 | } catch (IOException e) { 602 | } finally { 603 | if (bin != null) { 604 | try { 605 | bin.close(); 606 | } catch (Exception e) { 607 | } 608 | } 609 | if (fis != null) { 610 | try { 611 | fis.close(); 612 | } catch (Exception e) { 613 | } 614 | } 615 | } 616 | return charset; 617 | } 618 | 619 | // ========================================================================== 620 | public static void main(String[] args) { 621 | // String fromFile="F:/img/gogo.jpg"; 622 | // 623 | // byte[] arrByte = FileUtils.fileToBytes(fromFile); 624 | // System.out.println("size:"+(arrByte==null?0:arrByte.length)); 625 | // System.out.println("write:"+FileUtil.bytesToFile(toFile, arrByte)); 626 | 627 | } 628 | } 629 | --------------------------------------------------------------------------------