溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何實(shí)現(xiàn)API接口限流

發(fā)布時(shí)間:2021-12-30 10:53:30 來(lái)源:億速云 閱讀:189 作者:小新 欄目:大數(shù)據(jù)

這篇文章主要介紹如何實(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ù)。如下圖:

如何實(shí)現(xiàn)API接口限流

假如我們要求接口的調(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è)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

api
AI