限流
为啥要限流,大多数情况是服务器资源不足,短时间内大量流量请求到服务器,限流会有部分流量正常返回,部分流量异常,好过服务器宕机,所有流量异常返回的情况。
在代码世界上,限流有两种比较常见的算法:
1.令牌桶算法
2.漏桶算法
还有一种是滑动窗口限流,最早接触滑动窗口是TCP协议
滑动窗口
手写滑动窗口限流
设计思路:
Dqueue记录每次请求时间戳,每次计算时间窗口是否超过设定值,没有超出计数,超出清空Dqueue,计数归零,重新计算窗口。
简单的设计,单元测试可用,欢迎高手交流,此算法不推荐生产环境使用。
建议使用guvav 中限流算法
手写滑动窗口限流
public class slidingWindows {
/**
* 时间窗口
**/
private Long window;
/**
* 窗口的size 用于计算总的流量上限
**/
private Integer size = 2000;
/**
* 原子计数
**/
private AtomicInteger count;
/**
* Deque双队列
*/
private Deque<Long> timequeue;
public slidingWindows(Long windowCount) {
this.window = windowCount;
this.timequeue = new ConcurrentLinkedDeque<>();
this.count = new AtomicInteger(0);
timequeue.addFirst(System.currentTimeMillis());
}
/**
* 流量限制
**/
public Boolean tryout() {
Long now = System.currentTimeMillis();
if ((now - timequeue.peekLast()) > window) {
timequeue.clear();
timequeue.addFirst(now);
count.set(0);
} else {
if (count.addAndGet(1) < size) {
timequeue.add(now);
System.out.println("time: " + count.get());
return true;
}
}
return Boolean.FALSE;
}
public static void main(String[] args) {
slidingWindows ms = new slidingWindows(1000L);
// 创建固定大小的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(100); // 创建线程池内最大num个线程同时执行
// 创建线程
// 开始全部将线程加入线程池
// 执行num个线程同时执行
for (int i = 0; i < 100000; i++) {
Runnable r = new Runnable() {
public void run() {
try {
sleep(10);
System.out.println(ms.tryout());
Thread t = Thread.currentThread();
/*System.out.println(t.getName());
System.out.println(t.getName());*/
} catch (Exception e) {
e.printStackTrace();
}
}
};
threadPool.execute(r);
}
threadPool.shutdown();
}
}
应用实践
略