问题描述
某天A君突然发现自己的接口请求量突然涨到之前的10倍,没多久该接口几乎不可使用,并引发连锁反应导致整个系统崩溃。如何应对这种情况呢?
常见的限流算法
1、计数器:Java内部也可以通过原子类计数器AtomicIntege
// 限流的个数
private int maxCount = 10;
// 指定的时间内
private long interval = 60;
// 原子类计数器
private AtomicInteger atomicInteger = new AtomicInteger(0);
// 起始时间
private long startTime = System.currentTimeMillis();
public boolean limit(int maxCount, int interval) {
atomicInteger.addAndGet(1);
if (atomicInteger.get() == 1) {
startTime = System.currentTimeMillis();
atomicInteger.addAndGet(1);
return true;
}
// 超过了间隔时间,直接重新开始计数
if (System.currentTimeMillis() - startTime > interval * 1000) {
startTime = System.currentTimeMillis();
atomicInteger.set(1);
return true;
}
// 还在间隔时间内,check有没有超过限流的个数
if (atomicInteger.get() > maxCount) {
return false;
}
return true;
}
漏桶算法和令牌桶算法。
2、漏桶算法:漏桶算法思路很简单,请求先进入到漏桶里,漏桶以一定的速度出水,当水请求过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。
3、令牌桶算法:对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。如图2所示,令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。
3、redis + lua
https://mp.weixin.qq.com/s/sP4Hu8HdbVrE-Cm0uPPotg
限流工具类RateLimiter
google开源工具包guava提供了限流工具类RateLimiter,该类基于“令牌桶算法”,非常方便使用
package com.emax.paycenter.web.utils;
import com.google.common.util.concurrent.RateLimiter;
/**
* Created by wangxiaodong on 2019/9/6.
*/
public class RateLimiterDemo {
public static void main(String[] args) {
testNoRateLimiter();
testWithRateLimiter();
}
public static void testNoRateLimiter() {
Long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
new Thread(new Task(i, null)).start();
}
Long end = System.currentTimeMillis();
System.out.println(end - start);
}
public static void testWithRateLimiter() {
Long start = System.currentTimeMillis();
// RateLimiter limiter = RateLimiter.create(2); // 每秒不超过2个任务被提交 10个循环 大概5s执行完
RateLimiter limiter = RateLimiter.create(0.2); // 每秒不超过0.2个任务被提交 10个循环 大概50s执行完
for (int i = 0; i < 10; i++) {
new Thread(new Task(i, limiter)).start();
}
Long end = System.currentTimeMillis();
System.out.println(end - start);
}
static class Task implements Runnable {
private int i;
private RateLimiter limiter;
public Task(int i, RateLimiter limiter) {
this.i = i;
this.limiter = limiter;
}
@Override
public void run() {
if(limiter!=null){
limiter.acquire();//请求RateLimiter, 超过permits会被阻塞
}
System.out.println(System.currentTimeMillis()+"工人" + this.i + ":WORKING...");
}
}
}