1.限流基础知识介绍为什么需要限流,相信不用我多说。比如我周末去餐厅吃饭,但是人太多,只能去前台拿号,号是我的才能进餐厅吃饭。如果餐厅不限制流量怎么办?到了吃饭时间,人潮涌入,餐厅处理不了人流,很容易出事(餐厅人满为患,没路可走。餐厅员工崩溃我搞不定)回到代码世界也是一样。服务器可以处理的请求数量是有限的。如果请求量特别大,我们需要在代码中进行限流(要么让请求等待,要么抛出请求)在代码界,常见的限流算法有两种:令牌桶算法漏桶算法1.1什么是漏桶算法?能容纳的容量,再往桶里倒水,就会溢出(限流):目前我们能知道的是:桶的容量是固定的(图中绿色那块)超过桶的容量会溢出(要么等待,要么直接丢弃)OK,现在我们在桶上挖一个洞,让水从洞里流出来:桶的洞口大小是固定的,所以水从洞里流出来费率也是固定的。所以综上所述,算法需要的参数只有两个:桶的容量和漏水的速度。水。比如我的水桶容量是100L,但是我的水桶出水速度是10L/s。这时候如果有100L/s的水进来,我只让10L的水进桶,其他的都是有限的。(请求的速度是有限制的)允许一定的突流:我的水桶可以装100L,如果我的水桶现在是空的,那么100L的水可以全部进入我的水桶。我以10L/s的速度流出水。如果还有100L的水进来,只能限制流量。经过上面的分析,我们知道漏桶算法可以平滑网络上的突发流量(因为漏水的速率是固定的)1.2什么是令牌桶算法现在我还有一个桶,不是用来盛水的,用来存放令牌:令牌会按照一定的速率被扔进桶里,比如我1秒扔10个令牌到桶里:桶里能装的令牌数量是有上限的,比如我的桶你最多只能容纳1000个代币。每次有请求进来,我都会去桶里拿一个token。比如我这一秒有1001个请求,我就去桶里拿1001个token。这时候可能有两种情况:桶里没有1001个令牌了只有1000个令牌,所以得不到令牌的请求只能阻塞(等待)。桶中有1001个token,所有请求都可以执行。令牌桶算法支持网络突发流量**漏桶和令牌桶的区别:**从上面的例子,你大概可以看出漏桶只能以固定的速率处理请求,而cardbucket可以处理bucket中token数量上限的请求。2.RateLimiter的使用RateLimiter是Guava的一个限流组件。我的系统用的就是这个限流元件,用起来很方便。引入pom依赖:com.google.guavaguava20.0RateLimiter基于令牌桶算法,API非常简单,看下面的Demo:publicstaticvoidmain(String[]args){//线程池ExecutorServiceexec=Executors.newCachedThreadPool();//速率只有每秒3个permitsfinalRateLimiterrateLimiter=RateLimiter.create(3.0);for(inti=0;i<100;i++){finalintno=i;Runnablerunnable=newRunnable(){@Overridepublicvoidrun(){try{//获取权限rateLimiter.acquire();System.out.println("Accessing:"+no+",time:"+newSimpleDateFormat("yy-MM-ddHH:mm:ss").format(newDate()));}catch(Exceptione){e.printStackTrace();}}};//执行线程exec。execute(runnable);}//退出线程池exec.shutdown();}从结果我们可以看出每秒只执行3次:3.分布式限流RateLimiter是一个独立的限流组件,如果是对于分布式应用,我该怎么办?可以使用Redis+Lua来实现。一般lua脚本代码如下:localkey="rate.limit:"..KEYS[1]--限流KEYlocallimit=tonumber(ARGV[1])--限流大小localcurrent=tonumber(redis.call('得到',键)或“0”)我fcurrent+1>limitthen--如果超过当前限制return0else--请求数+1,并设置1秒过期redis.call("INCRBY",key,"1")redis.call("expire",key,"1")返回current+1Java代码如下:publicstaticbooleanaccquire()throwsIOException,URISyntaxException{Jedisjedis=newJedis("127.0.0.1");FileluaFile=newFile(RedisLimitRateWithLUA.class.getResource("/").toURI().getPath()+"limit.lua");StringluaScript=FileUtils.readFileToString(luaFile);Stringkey="ip:"+System.currentTimeMillis()/1000;//当前秒Stringlimit="5";//最大限制Listkeys=newArrayList();keys.add(key);Listargs=newArrayList();args.add(limit);Longresult=(Long)(jedis.eval(luaScript,keys,args));//执行lua脚本,传入参数returnresult==1;}说明:Java代码将key和最大限制参数传入lua脚本执行lua脚本(lua脚本判断当前key是否超过最大限制)如果超过,返回0(限流)如果没有,返回1(程序继续执行)