java單機(jī)接口限流處理方案詳解
對(duì)單機(jī)服務(wù)做接口限流的處理方案
簡(jiǎn)單說(shuō)就是設(shè)定某個(gè)接口一定時(shí)間只接受固定次數(shù)的請(qǐng)求,比如/add接口1秒最多接收100次請(qǐng)求,多的直接拒絕,這個(gè)問(wèn)題很常見(jiàn),場(chǎng)景也好理解,直接上代碼:
/** * 單機(jī)限流 */ @Slf4j public class FlowLimit { //接口限流上限值和限流時(shí)間緩存 private static Cache<String, AtomicLong> localCache = CacheBuilder.newBuilder().maximumSize(100) .expireAfterWrite(1000, TimeUnit.MILLISECONDS).build(); //每個(gè)接口的上限緩存 private static Map<String, Long> maxFlowLimitMap = new ConcurrentHashMap<>(); private static final FlowLimit instance = new FlowLimit(); //這塊的目的是初始化每個(gè)接口的上限,下面的變量:apiFlowLimitConfigure //實(shí)際使用的時(shí)候應(yīng)該是從db或者其他地方獲取設(shè)置的每個(gè)接口的限流上限值, //這樣可以動(dòng)態(tài)的調(diào)整接口上限,比如直接修改db,不用發(fā)布,就可以調(diào)整接口限流值 static { new ScheduledThreadPoolExecutor(1, runnable -> { Thread thread = new Thread(runnable, "api-flowLimit-configure"); // thread.setDaemon(true); return thread; }).scheduleAtFixedRate(() -> { try { String apiFlowLimitConfigure = "{\"doAdd\":100}"; //表示/doAdd接口1秒接受100次請(qǐng)求 Map mapObj = JSONObject.parseObject(apiFlowLimitConfigure, Map.class); if(mapObj != null){ mapObj.forEach((key, value) -> { if(value != null){ instance.setMaxFlowLimit(key.toString(), new Long(value.toString())); }else{ log.warn(key + " - 設(shè)置接口限流發(fā)現(xiàn)限流值為空,設(shè)置默認(rèn)值"); instance.setMaxFlowLimit(key.toString(), 100L); } }); } } catch (Exception e) { log.error("設(shè)置接口限流出現(xiàn)異常{}", e); } }, 0, 3, TimeUnit.SECONDS); } public static FlowLimit getInstance() { return instance; } private FlowLimit setMaxFlowLimit(String key, Long maxFlowLimit) { maxFlowLimitMap.put(key, maxFlowLimit); return this; } public Boolean isAvailable(String key) { return checkAvailable(key, 1L); } public Boolean isAvailable(String key, Long incrNum) { return checkAvailable(key, incrNum); } private Boolean checkAvailable(String key, Long incrNum){ Long maxFlowLimit = maxFlowLimitMap.get(key); if (null == maxFlowLimit || maxFlowLimit == 0) { return true; } if (incrAndGet(key, incrNum) <= maxFlowLimit.longValue()) { return true; } else { return false; } } private long incrAndGet(String key, final long n) { try { return localCache.get(key, new Callable<AtomicLong>() { @Override public AtomicLong call() throws Exception { return new AtomicLong(0); } }).addAndGet(n); } catch (Exception e) { log.error(e.getMessage(), e); } return 0; } public long get(String key) { return incrAndGet(key, 0); } }
上面這個(gè)就是單機(jī)限流邏輯,代碼不難,感覺(jué)沒(méi)必要使用ConcurrentHashMap,不過(guò)感覺(jué)無(wú)所謂了
這段代碼只需要加在需要限流的接口前面:
@GetMapping("doAdd") public Boolean doAdd(){ FlowLimit instance = FlowLimit.getInstance(); //單例獲取 //查看當(dāng)前的/doAdd接口是否觸發(fā)了限流 Boolean flowLimitFlag = instance.isAvailable("doAdd"); if(!flowLimitFlag){ log.warn("觸發(fā)限流,拒絕請(qǐng)求"); return false; } //doAdd() return true; }
調(diào)用實(shí)例如上
上面這個(gè)限流其實(shí)是有一定問(wèn)題的:比如你限定10秒鐘1000次,在第9.9秒的時(shí)候,突然進(jìn)來(lái)1000個(gè)請(qǐng)求,然后第10.1秒的時(shí)候,攻擊者,又進(jìn)來(lái)1000次請(qǐng)求,這樣,0.2秒之內(nèi),進(jìn)來(lái)2000次請(qǐng)求。。。
所以這個(gè)時(shí)候就需要令牌桶或者其他算法了,其他算法后面再寫(xiě)
沒(méi)怎么仔細(xì)測(cè)試,有問(wèn)題歡迎提出,共同學(xué)習(xí)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
@Valid 無(wú)法校驗(yàn)List<E>的問(wèn)題
這篇文章主要介紹了@Valid 無(wú)法校驗(yàn)List<E>的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Java線程協(xié)調(diào)運(yùn)行操作實(shí)例詳解
這篇文章主要介紹了Java線程協(xié)調(diào)運(yùn)行操作,結(jié)合具體實(shí)例形式詳細(xì)分析了Java線程協(xié)調(diào)運(yùn)行原理、實(shí)現(xiàn)方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-09-09Java在Excel中創(chuàng)建多級(jí)分組、折疊或展開(kāi)分組的實(shí)現(xiàn)
這篇文章主要介紹了Java在Excel中創(chuàng)建多級(jí)分組、折疊或展開(kāi)分組的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05spring?controller層引用service報(bào)空指針異常nullpointExceptio問(wèn)題
這篇文章主要介紹了spring?controller層引用service報(bào)空指針異常nullpointExceptio問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02詳解SpringBoot配置devtools實(shí)現(xiàn)熱部署
本篇文章主要介紹了詳解SpringBoot配置devtools實(shí)現(xiàn)熱部署 ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05Java數(shù)據(jù)結(jié)構(gòu)之雙向鏈表的實(shí)現(xiàn)
相較單鏈表,雙向鏈表除了data與next域,還多了一個(gè)pre域用于表示每個(gè)節(jié)點(diǎn)的前一個(gè)元素。這樣做給雙向鏈表帶來(lái)了很多優(yōu)勢(shì)。本文主要介紹了雙向鏈表的實(shí)現(xiàn),需要的可以參考一下2022-10-10使用eclipse + maven一步步搭建SSM框架教程詳解
SSM(Spring+SpringMVC+MyBatis)框架集由Spring、SpringMVC、MyBatis三個(gè)開(kāi)源框架整合而成,常作為數(shù)據(jù)源較簡(jiǎn)單的web項(xiàng)目的框架.這篇文章主要介紹了eclipse + maven搭建SSM框架 ,需要的朋友可以參考下2017-11-11