现在,微服务大行其道,好多项目都是拆分,再拆分,美其名曰:解耦!这个不多说,适合自己的实际项目就好,反正项目都是会逐渐演进。
不过,无论哪种服务,一旦用户量大了,请求多了,服务器压力就会很大,有时候,可能直接把服务器搞崩溃了。于是,我们常用的一些中间件或者是一些策略方法,便进入到了项目之中。比如:消息中间件的削峰填谷,布隆过滤器的拦截鉴定,Nginx的轮询转发等等等等。
今天,就来说一个中间件常用的限流算法,木桶算法!
简单描述一下,其实也就是类比。还记得小时候的数学题么?一个游泳池里面,一个管道放水,一个抽水机抽水,管道放水的速度是多少,抽水机抽水的速度是多少,然后,什么时候游泳池会被放满水之类的。
漏桶算法,和这个类似,就是把流量比作水流,流量现进入到木桶中,然后木桶下面有一个洞,当然,洞的大小(流速)可以自己控制。这样就可以防止流量蜂拥而至,压垮服务器。
客户端发送请求,先要进入木桶中,短暂缓存后,从木桶的洞口周流出。这样就可以保证流量的匀速性,减少服务器的压力。
那如果木桶满了呢?满了,就拒绝呗!再多就hold不住了,那你等会再来呗~
下面简单的看下木桶算法,上个简单代码。
import java.util.Random;
import java.util.concurrent.*;
/**
* desc: 漏桶算法
* author:zhang
*/
public class BucketTest {
// 漏桶算法,桶其实就是一个缓存,一个计数(相当于多少滴水),简单点,直接存个数字吧
private static volatile long bucket = 666 ;
// 初始化,刚开始桶里没有水(0滴)
private static long water = 0 ;
// 速度,滴6滴水/s.这个是水桶自动的. 本来想定毫秒的,太快了。。
private static long rate = 6 ;
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
//启动一个定时器吧,2个线程的
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
//线程任务是加水,200毫秒加一次
executor.scheduleAtFixedRate(() -> {
//先定义下加多少水吧,水量肯定是随机的,定一个呗
Random random = new Random();
int addWater = random.nextInt(500);
System.out.println("加水量:"+addWater);
//看下桶里还有多少水呗,满了就不给装了。这个时候就用到了二年级知识了,速率*时间 = 水量。
//哦,水量不能是负值,限制下
water = Math.max(0, water - (System.currentTimeMillis()-start) * rate/1000);
if ((water + addWater) > bucket) {
//水溢出了,肯定要结束啊,不阻塞了
System.out.println("水满了");
countDownLatch.countDown();
} else {
System.out.println("水量:"+water + ",继续加水!");
water = water + addWater;
}
}, 0,200,TimeUnit.MICROSECONDS);
// 阻塞线程
countDownLatch.await();
// 这个之前漏掉了,主线程关闭,定时器并没有关掉
executor.shutdownNow();
}
}
我这个加水还是有点慢,加了好久才结束,本地调试的时候,可以改下addWater,调大一下数字,这样就会快点结束。
看下结果哈~
no sacrifice,no victory~