只有光头才能变强。java
文本已收录至个人GitHub仓库,欢迎Star:github.com/ZhongFuChen…git
以前在学习的时候也接触不到高并发/大流量这种东西,因此限流固然是没接触过的了。在看公司项目的时候,发现有用到限流(RateLimiter),顺带了解一波。github
为啥要限流,相信就不用我多说了。redis
在代码世界上,限流有两种比较常见的算法:算法
好比,如今我有一个桶子,绿色那块是我能装水的容量,若是超过我能装下的容量,再往桶子里边倒水,就会溢出来(限流):segmentfault
咱们目前能够知道的是:服务器
OK,如今咱们在桶子里挖个洞,让水能够从洞子里边流出来:网络
桶子的洞口的大小是固定的,因此水从洞口流出来的速率也是固定的。并发
因此总结下来算法所需的参数就两个:分布式
漏桶算法有两种实现:
通过上面的分析咱们就知道:
漏桶算法能够平滑网络上的突发流量(由于漏水的速率是固定的)
如今我有另一个桶子,这个桶子不用来装水,用来装令牌:
令牌会必定的速率扔进桶子里边,好比我1秒扔10个令牌进桶子:
桶子能装令牌的个数有上限的,好比个人桶子最多只能装1000个令牌。
每一个请求进来,就会去桶子拿一个令牌
令牌桶算法支持网络上的突发流量
**漏桶和令牌桶的区别:**从上面的例子估计你们也能看出来了,漏桶只能以固定的速率去处理请求,而令牌桶能够以桶子最大的令牌数去处理请求
RateLimiter是Guava的一个限流组件,我这边的系统就有用到这个限流组件,使用起来十分方便。
引入pom依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
复制代码
RateLimiter它是基于令牌桶算法的,API很是简单,看如下的Demo:
public static void main(String[] args) {
//线程池
ExecutorService exec = Executors.newCachedThreadPool();
//速率是每秒只有3个许可
final RateLimiter rateLimiter = RateLimiter.create(3.0);
for (int i = 0; i < 100; i++) {
final int no = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
//获取许可
rateLimiter.acquire();
System.out.println("Accessing: " + no + ",time:"
+ new SimpleDateFormat("yy-MM-dd HH:mm:ss").format(new Date()));
} catch (Exception e) {
e.printStackTrace();
}
}
};
//执行线程
exec.execute(runnable);
}
//退出线程池
exec.shutdown();
}
复制代码
咱们能够从结果看出,每秒只能执行三个:
RateLimiter是一个单机的限流组件,若是是分布式应用的话,该怎么作?
可使用Redis+Lua的方式来实现,大体的lua脚本代码以下:
local key = "rate.limit:" .. KEYS[1] --限流KEY
local limit = tonumber(ARGV[1]) --限流大小
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then --若是超出限流大小
return 0
else --请求数+1,并设置1秒过时
redis.call("INCRBY", key,"1")
redis.call("expire", key,"1")
return current + 1
end
复制代码
Java代码以下:
public static boolean accquire() throws IOException, URISyntaxException {
Jedis jedis = new Jedis("127.0.0.1");
File luaFile = new File(RedisLimitRateWithLUA.class.getResource("/").toURI().getPath() + "limit.lua");
String luaScript = FileUtils.readFileToString(luaFile);
String key = "ip:" + System.currentTimeMillis()/1000; // 当前秒
String limit = "5"; // 最大限制
List<String> keys = new ArrayList<String>();
keys.add(key);
List<String> args = new ArrayList<String>();
args.add(limit);
Long result = (Long)(jedis.eval(luaScript, keys, args)); // 执行lua脚本,传入参数
return result == 1;
}
复制代码
解释:
参考来源:
更多资料参考:
乐于输出干货的Java技术公众号:Java3y。公众号内有200多篇原创技术文章、海量视频资源、精美脑图,关注便可获取!
以为个人文章写得不错,点赞!