您好,登錄后才能下訂單哦!
這篇文章主要介紹如何實(shí)現(xiàn)API接口限流,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
同樣,在互聯(lián)網(wǎng)行業(yè)中,也存在這樣得場(chǎng)景,我們把它稱為——限流,為什么要限流呢,原因如下:
在系統(tǒng)上線初期,用戶量和訪問(wèn)量不大得時(shí)候,一般部署幾臺(tái)應(yīng)用服務(wù)器,數(shù)據(jù)庫(kù)做一個(gè)讀寫(xiě)分離就基本上抗得住,但隨著時(shí)間的推移,業(yè)務(wù)的發(fā)展,用戶量和日活得增加,系統(tǒng)所承受的壓力越來(lái)越大,我么都知道,應(yīng)用服務(wù)器擴(kuò)容很方便,但數(shù)據(jù)庫(kù)擴(kuò)容就有些麻煩,如果請(qǐng)求數(shù)據(jù)庫(kù)的量太大的話,或者遇到惡意攻擊,數(shù)據(jù)庫(kù)極有可能宕機(jī),最后導(dǎo)致整個(gè)網(wǎng)站不可用。
為了避免這樣的事情發(fā)生,我們通常要限制住用戶的請(qǐng)求次數(shù)。對(duì)于合法的用戶,我們要限制單位時(shí)間內(nèi)用戶調(diào)用接口的次數(shù),對(duì)于惡意攻擊者,直接將他放入黑名單中。
我們通常用Redis中的zset數(shù)據(jù)結(jié)構(gòu)來(lái)做限流。由于zset底層是用跳躍表存儲(chǔ)數(shù)據(jù)的,按score字段從小到大排序,我們可以把時(shí)間戳存入score字段。隨著時(shí)間的流逝,把每一次請(qǐng)求的時(shí)間戳存入score中,而member呢?隨便存儲(chǔ)什么無(wú)所謂啦,但不要存儲(chǔ)太大的數(shù)據(jù)。如下圖:
假如我們要求接口的調(diào)用一分鐘不能超過(guò)100次,那么圖中橙色矩形區(qū)域是一個(gè)時(shí)間段范圍內(nèi)的時(shí)間窗口,矩形的有邊框是當(dāng)前時(shí)間,它隨著時(shí)間的流逝一點(diǎn)一點(diǎn)向右移動(dòng),矩形左邊框是1min前,矩形的寬度就是1min。我們通過(guò)Redis的zcount命令很容易計(jì)算出這個(gè)時(shí)間范圍內(nèi)的數(shù)據(jù)量,如果數(shù)據(jù)量超出100,說(shuō)明用戶請(qǐng)求的太快了,應(yīng)該進(jìn)行限流的操作。下面來(lái)看一段簡(jiǎn)單的代碼:
/**
* 基于zset限流
* @param key redis key
* @param maxCount 指定時(shí)間內(nèi)最大通過(guò)個(gè)數(shù)
* @param timeRange 時(shí)間窗范圍,單位:秒
* @return 是否可以繼續(xù)請(qǐng)求
*/
public static boolean rateLimiterByZset(String key,int maxCount,int timeRange){
try (Jedis jedis = jedisPool.getResource()) {
long currentTime = new Date().getTime(); //當(dāng)前時(shí)間戳
long secondTime = currentTime-timeRange*1000; //second秒前的時(shí)間戳
long memberCount = jedis.zcount(key,secondTime,currentTime);
if(memberCount >= maxCount){
return false;
}
jedis.zadd(key,currentTime,currentTime+"");
//刪除時(shí)間框外的數(shù)據(jù),因?yàn)樗鼈円呀?jīng)沒(méi)有用了
jedis.zremrangeByScore(key,0,secondTime);
}
return true;
}
哈哈,就這么簡(jiǎn)單。有一點(diǎn)要注意的是,請(qǐng)大家及時(shí)刪除時(shí)間框(也就是上圖中左邊框外)范圍外的數(shù)據(jù),因?yàn)樗鼈円呀?jīng)沒(méi)有用了,留著非常消耗內(nèi)存資源。
大家會(huì)不會(huì)有這樣的疑問(wèn):對(duì)zset操作這么頻繁,會(huì)不會(huì)有性能上的問(wèn)題呀?其實(shí)大家不必太擔(dān)心,畢竟Redis的數(shù)據(jù)結(jié)構(gòu)都是經(jīng)過(guò)精心設(shè)計(jì)的,性性能很高,大家可以參考小編的這篇文章,來(lái)學(xué)習(xí)Redis的數(shù)據(jù)結(jié)構(gòu)。
從數(shù)據(jù)存儲(chǔ)角度分析Redis性能為何如此高
劉蒞,公眾號(hào):向代碼致敬從數(shù)據(jù)存儲(chǔ)角度分析Redis性能為何如此高
測(cè)試程序和輸出效果如下:
@Test public void test1() throws Exception{ int i = 1; while(true){ Thread.sleep(5); boolean flag = RedisRateLimiter.rateLimiterByZset("keysss",10,1); if(flag){ System.out.println("第"+i+"個(gè)請(qǐng)求成功"); }else{ System.out.println("第"+i+"個(gè)被限流"); } i++; } }
第1個(gè)請(qǐng)求成功第2個(gè)請(qǐng)求成功第3個(gè)請(qǐng)求成功第4個(gè)請(qǐng)求成功第5個(gè)請(qǐng)求成功第6個(gè)請(qǐng)求成功第7個(gè)請(qǐng)求成功第8個(gè)請(qǐng)求成功第9個(gè)請(qǐng)求成功第10個(gè)請(qǐng)求成功第11個(gè)被限流第12個(gè)被限流...第42個(gè)被限流第43個(gè)被限流第44個(gè)請(qǐng)求成功第45個(gè)請(qǐng)求成功第46個(gè)請(qǐng)求成功第47個(gè)請(qǐng)求成功第48個(gè)請(qǐng)求成功第49個(gè)請(qǐng)求成功第50個(gè)請(qǐng)求成功第51個(gè)請(qǐng)求成功第52個(gè)請(qǐng)求成功第53個(gè)請(qǐng)求成功第54個(gè)被限流第55個(gè)被限流...
以上是“如何實(shí)現(xiàn)API接口限流”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。