/** * 计数器限流 * 计 数器法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说, * 我们1分钟的访问次数不能超过100个。那么我们可以这么做:在一开 始的时候, * 我们可以设置一个计数器counter,每当一个请求过来的时候,counter就加1, * 如果counter的值大于100并且该请求与第一个 请求的间隔时间还在1分钟之内,那么说明请求数过多; * 如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置 counter, * * * 缺陷:假设有一个恶意用户,他在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求, * * 那么其实这个用户在 1秒里面,瞬间发送了200个请求。我们刚才规定的是1分钟最多100个请求, * * 也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们的速率限制。 * * 用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。 */ private static long timestamp = System.currentTimeMillis(); //限制1S内100个请求 private static long limitcount = 100; //间隔 private static long interval = 1000; //请求数量 private static long requestCount = 0; public static boolean grant(){ long now = System.currentTimeMillis(); if(now < timestamp + interval){ if(requestCount < limitcount){ ++requestCount; return true; }else { return false; } }else { timestamp = System.currentTimeMillis(); requestCount = 0; return false; } } @Test public void testLimit(){ for (int i = 0; i < 500; i++) { new Thread(new Runnable() { @Override public void run() { if(grant()){ System.out.println("执行业务"); }else { System.out.println("限流"); } } }).start(); } } /** * 漏桶算法 * 首先,我们有一个固定容量的桶,有水流进来,也有水流出去。对于流进来的水来说,我们无法预计一共有多 * 少水会流进来,也无法预计水流的速度。但是对于流出去的水来说,这个桶可以固定水流出的速率。而且, * 当桶满了之后,多余的水将会溢出。 * * 我们将算法中的水换成实际应用中的请求,我们可以看到漏桶算法天生就限制了请求的速度。 * 当使用了漏桶算法,我们可以保证接口会以一个常速速率来处理请求。所以漏桶算法天生不会出现临界问题。 */ //时间刻度 private static long time = System.currentTimeMillis(); //桶里面现存水 private static int water = 0; //桶的大小 private static int size = 10; //出水速度 private static int rate = 3; public static boolean grant02(){ long now = System.currentTimeMillis(); int out = (int) ((now - time) * rate); water = Math.max(0,water - out); time = now; if((water + 1) < size){ ++water; return true; }else { return false; } } @Test public void testLimit02(){ for (int i = 0; i < 500 ; i++) { new Thread(new Runnable() { @Override public void run() { if(grant02()){ System.out.println("执行业务"); }else { System.out.println("限流"); } } }).start(); } } /** * 令牌桶算法 * 首先,我们有一个固定容量的桶,桶里存放着令牌(token)。桶一开始是空的,token以 * 一个固定的速率r往桶里填充,直到达到桶的容量,多余的令牌将会被丢弃。每当一个请求过来时, * 就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通 过。 * * 漏桶的出水速度是恒定的,那么意味着如果瞬时大流量的话,将有大部分请求被丢弃掉(也就是所谓的溢出)。 * 为了解决这个问题,令牌桶进行了算法改进 */ //private static long time = System.currentTimeMillis(); //令牌流速 private static int createTokenRate = 3; private static int sizeToken = 10; //当前令牌数 private static int tokens = 0; public static boolean grant03(){ long now = System.currentTimeMillis(); int in = (int) ((now - time) * createTokenRate); tokens = Math.min(sizeToken,tokens + in); time = now; if(tokens > 0){ --tokens; return true; }else { return false; } } @Test public void testLimit03(){ for (int i = 0; i < 500 ; i++) { new Thread(new Runnable() { @Override public void run() { if(grant03()){ System.out.println("执行业务"); }else { System.out.println("限流"); } } }).start(); } }