Java中實(shí)現(xiàn)接口限流的方案詳解
前言
今天看技術(shù)文章時(shí)看到一篇文章在討論接口限流問題,覺得業(yè)務(wù)場景經(jīng)常會使用到,就思考了一下如果是我會怎么實(shí)現(xiàn)?常用方案就是計(jì)數(shù)器限流方案、時(shí)間窗口限流方案和令牌桶限流方案等。這些方案的概念大致也知道,但是實(shí)際上也沒有實(shí)現(xiàn)過,于是就自己試試實(shí)現(xiàn)邏輯,先動手再說。
場景
假設(shè)我們的場景是:
接口10秒內(nèi)最多請求3次
思路
我想到的實(shí)現(xiàn)思路是有點(diǎn)時(shí)間窗口方案的樣子,大致就是緩存請求時(shí)間,然后在每次請求的時(shí)候去校驗(yàn)10秒內(nèi)是否超過3次請求。
圖示
假設(shè)下面是請求的時(shí)間(單位:秒),那么前三次都可以請求成功,一個(gè)大小為3的數(shù)組緩存數(shù)據(jù)cache就是紅框所示:

接下去的第四個(gè)請求、第五個(gè)請求和第一個(gè)緩存時(shí)間對比,發(fā)現(xiàn)都小于10秒,則請求不通過。

第六個(gè)請求時(shí)間和第一個(gè)請求時(shí)間對比,時(shí)間大于10秒,所以請求通過,然后更新緩存數(shù)據(jù)cache,緩存的第一個(gè)數(shù)據(jù)改為第二個(gè)請求時(shí)間,緩存第二個(gè)數(shù)據(jù)改為第三個(gè)請求時(shí)間,緩存第三個(gè)數(shù)據(jù)改為第6個(gè)請求時(shí)間,如下圖藍(lán)框所示,依次類推校驗(yàn)每次請求是否符合場景要求。

實(shí)現(xiàn)
1.聲明一個(gè)數(shù)組cache(數(shù)組大小可以為最大請求次數(shù)3),儲存請求時(shí)間;
因?yàn)槲覀円獙Ρ日埱髸r(shí)間,所以講請求緩存下來可以節(jié)省查數(shù)據(jù)庫的時(shí)間。因?yàn)閳鼍罢f明限制3次,所以我們只需要知道最后3個(gè)請求的時(shí)間就可以完成校驗(yàn),之前的數(shù)據(jù)可以忽略。
2.首先是初始化情況,當(dāng)請求小于3的時(shí)候,請求直接通過,并緩存當(dāng)前請求時(shí)間;
3.當(dāng)請求大于等于3的時(shí)候,判斷當(dāng)前請求時(shí)間和倒數(shù)第三次請求(cache[0]的數(shù)據(jù))的間隔時(shí)間是否超過10秒,如果是則通過,否則通過。
通過的時(shí)候,要更新緩存數(shù)據(jù),將數(shù)據(jù)前移,這次請求時(shí)間就是最后一次請求時(shí)間。
// 更新緩存 cache.set(0, cache.get(1)); cache.set(1, cache.get(2)); cache.set(2, now);
4.最后執(zhí)行下代碼驗(yàn)證下結(jié)果
完整代碼如下所示:
package demo;
import cn.hutool.core.date.DateUtil;
import lombok.SneakyThrows;
import lombok.val;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
/**
* 限流示例,每分鐘只能請求3次
*/
public class LimitFlowDemo {
// 記錄最后3次請求時(shí)間
List<Long> cache = new ArrayList<>();
int limitTime = 10;
public boolean validate() {
boolean res = false;
val now = DateUtil.currentSeconds();
System.out.println("當(dāng)前時(shí)間:" + DateUtil.format(new Date(now * 1000), "HH:mm:ss"));
if (cache.size() < 3) {
res = true;
cache.add(now);
} else if(now - cache.get(0) > limitTime){
res = true;
// 更新緩存
cache.set(0, cache.get(1));
cache.set(1, cache.get(2));
cache.set(2, now);
}
System.out.println("請求結(jié)果:" + res);
return res;
}
@SneakyThrows
public static void main(String[] args) {
Random rd = new Random();
LimitFlowDemo demo = new LimitFlowDemo();
for (int i = 0; i < 1000; i++) {
demo.validate();
// Thread.sleep(rd.nextInt(3000));
Thread.sleep(500);
}
}
}到此這篇關(guān)于Java中實(shí)現(xiàn)接口限流的方案詳解的文章就介紹到這了,更多相關(guān)Java接口限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何通過自定義spring?invalidator注解校驗(yàn)數(shù)據(jù)合法性
Spring mvc如何實(shí)現(xiàn)數(shù)據(jù)處理
SpringCloud使用集中配置組件Config規(guī)避信息泄露
SpringBoot在 POM 中引入本地 JAR 包的方法
Java實(shí)現(xiàn)TCP和UDP協(xié)議詳解

