只有光头才能变强。
文本已收录至我的github仓库,欢迎star:https://github.com/zhongfucheng3y/3y
之前在学习的时候也接触不到高并发/大流量这种东西,所以限流当然是没接触过的了。在看公司项目的时候,发现有用到限流(ratelimiter),顺带了解一波。
为啥要限流,相信就不用我多说了。
在代码世界上,限流有两种比较常见的算法:
比如,现在我有一个桶子,绿色那块是我能装水的容量,如果超过我能装下的容量,再往桶子里边倒水,就会溢出来(限流):
我们目前可以知道的是:
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多篇原创技术文章、海量视频资源、精美脑图,关注即可获取!
觉得我的文章写得不错,点赞!
如对本文有疑问, 点击进行留言回复!!
[杭电多校2020]第一场 1004 Distinct Sub-palindromes
Swift -- 将本地生成的UIImage进行持久化保存(存到文件中fileManager.createFile)
网友评论