限流
爲啥要限流,大多數情況是服務器資源不足,短時間內大量流量請求到服務器,限流會有部分流量正常返回,部分流量異常,好過服務器宕機,所有流量異常返回的情況。
在代碼世界上,限流有兩種比較常見的算法:
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();
}
}
應用實踐
略