• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當前位置: 首頁 - 科技 - 知識百科 - 正文

    淺談Koa服務限流方法實踐

    來源:懂視網 責編:小采 時間:2020-11-27 22:27:20
    文檔

    淺談Koa服務限流方法實踐

    淺談Koa服務限流方法實踐:最近接了一個需求,很簡單,就是起一個server,收到請求時調用某個提供好的接口,然后把結果返回。因為這個接口的性能問題,同時在請求的不能超過特定數目,要在服務中進行限流。 限流的要求是,限制同時執行的數目,超出這個數目后要在一個隊列中進行緩存。
    推薦度:
    導讀淺談Koa服務限流方法實踐:最近接了一個需求,很簡單,就是起一個server,收到請求時調用某個提供好的接口,然后把結果返回。因為這個接口的性能問題,同時在請求的不能超過特定數目,要在服務中進行限流。 限流的要求是,限制同時執行的數目,超出這個數目后要在一個隊列中進行緩存。

    最近接了一個需求,很簡單,就是起一個server,收到請求時調用某個提供好的接口,然后把結果返回。因為這個接口的性能問題,同時在請求的不能超過特定數目,要在服務中進行限流。

    限流的要求是,限制同時執行的數目,超出這個數目后要在一個隊列中進行緩存。

    koa 中間件不調用 next

    最初的想法是在 koa 中間件中進行計數,超過6個時將next函數緩存下來。等正在進行中的任務結束時,調用next繼續其他請求。

    之后發現 koa 中間件中,不執行next函數請求并不會停下,而是不再調用之后的中間件,直接返回內容。

    const Koa = require('koa');
    const app = new Koa();
    app.use((ctx, next) => {
     console.log('middleware 1');
     setTimeout(() => {
     next();
     }, 3000);
     ctx.body = 'hello';
    });
    app.use((ctx, next) => {
     console.log('middleware 2');
    });
    app.listen(8989);

    以上代碼首先在控制臺打出 ‘middleware 1' => 瀏覽器收到 ‘hello' => 控制臺打出 ‘middleware 2'。

    這里還有一個要注意的地方,就是一個請求已經結束(finish)后,他的next方法還是可以繼續調用,之后的middleware還是繼續運行的(但是對ctx的修改不會生效,因為請求已經返回了)。同樣,關閉的請求(close)也是同樣的表現。

    使用 await 讓請求進行等待

    延遲next函數執行不能達到目的。接下來自然想到的就是使用await讓當前請求等待。await的函數返回一個Promise,我們將這個Promise中的resolve函數存儲到隊列中,延遲調用。

    const Koa = require('koa');
    const app = new Koa();
    const queue = [];
    app.use(async (ctx, next) => {
     setTimeout(() => {
     queue.shift()();
     }, 3000);
     await delay();
     ctx.body = 'hello';
    });
    function delay() {
     return new Promise((resolve, reject) => {
     queue.push(resolve);
     });
    }
    app.listen(8989);

    上面這段代碼,在delay函數中返回一個Promise,Promise的resolve函數存入隊列中。設置定時3s后執行隊列中的resolve函數,使請求繼續執行。

    針對路由進行限流,還是針對請求進行限流?

    限流的基本原理實現后,下面一個問題就是限流代碼該寫在哪里?基本上,有兩個位置:

    針對接口進行限流

    由于我們的需求中,限流是因為要請求接口的性能有限。所以我們可以單獨針對這個請求進行限流:

    async function requestSomeApi() {
     // 如果已經超過了最大并發
     if (counter > maxAllowedRequest) {
     await delay();
     }
     counter++;
     const result = await request('http://some.api');
     counter--;
     queue.shift()();
     return result;
    }
    

    下面還有一個方便復用的版本。

    async function limitWrapper(func, maxAllowedRequest) {
     const queue = [];
     const counter = 0;
     return async function () {
     if (counter > maxAllowedRequest) {
     await new Promise((resolve, reject) => {
     queue.push(resolve);
     });
     }
     counter++;
     const result = await func();
     counter--;
     queue.shift()();
     return result;
     }
    }
    

    針對路由進行限流

    這種方式是寫一個koa中間件,在中間件中進行限流:

    async function limiter(ctx, next) => {
     // 如果超過了最大并發數目
     if (counter >= maxAllowedRequest) {
     // 如果當前隊列中已經過長
     await new Promise((resolve, reject) => {
     queue.push(resolve);
     });
     }
     store.counter++;
     await next();
     store.counter--;
     queue.shift()();
    };
    

    之后針對不同路由在router中使用這個中間件就好了:

    router.use('/api', rateLimiter);

    比較

    實現了針對接口進行限流,覺得邏輯有些亂,于是改用了針對路由進行限流,一切運行的很完美。

    直到我又接了個需求,是要請求三次這個接口返回三次請求的結果數組。現在問題來了,我們不能直接調用接口,因為要限流。也不能直接調用請求接口的函數因為我們的限流是以路由為單位的。那怎么辦呢?我們只有請求這個路由了,自己請求自己。。

    需要注意的地方

    監聽close事件,將請求從隊列中移出
    已經存儲在隊列中的請求,有可能遇到用戶取消的情況。前面說過koa中即使請求取消,之后的中間件還是會運行,也就是還會執行需要限流的接口,造成浪費。

    可以監聽close事件來達到這個目的,每個請求我們需要用hash值來標記:

    ctx.res.on('close', () => {
     const index = queue.findIndex(item => item.hash === hash);
     if (index > -1) {
     queue.splice(index, 1);
     }
    });

    設置超時時間

    為了防止用戶等待過長時間,需要設置超時時間,這在koa中很容易實現:

    const server = app.listen(config.port);
    server.timeout = DEFAULT_TIMEOUT;

    當前隊列已經過長

    如果當前隊列已經過長了,即使加入隊列中也會超時。因此我們還需要處理隊列過長的情況:

    if (queue.length > maxAllowedRequest) {
     ctx.body = 'error message';
     return;
    }
    

    聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    淺談Koa服務限流方法實踐

    淺談Koa服務限流方法實踐:最近接了一個需求,很簡單,就是起一個server,收到請求時調用某個提供好的接口,然后把結果返回。因為這個接口的性能問題,同時在請求的不能超過特定數目,要在服務中進行限流。 限流的要求是,限制同時執行的數目,超出這個數目后要在一個隊列中進行緩存。
    推薦度:
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 精品国产亚洲男女在线线电影| 四虎影视永久在线观看精品| 亚洲国产一成久久精品国产成人综合 | 免费视频精品一区二区三区| 最新精品国偷自产在线| www夜片内射视频日韩精品成人| 国产精品一级香蕉一区| 久久久久成人精品无码中文字幕| 日韩精品欧美| 国产在线91精品入口| 999国产精品色在线播放| 999成人精品视频在线| 国产精品一区二区av| 久久精品九九亚洲精品| 亚洲精品无码久久久久| 亚洲精品乱码久久久久久蜜桃 | 一本色道久久88综合日韩精品| 黑人巨茎精品欧美一区二区| 99国内精品久久久久久久| 国产精品久久久久久久| 99久久99这里只有免费费精品| 日韩欧精品无码视频无删节| 一级做a爰黑人又硬又粗免费看51社区国产精品视 | 精品亚洲成AV人在线观看| 亚洲精品一级无码鲁丝片| 欧美日韩精品| 久久www免费人成精品香蕉| 国产在线精品一区二区高清不卡| 久热这里只精品99re8久| 国产精品高清一区二区人妖| 囯产精品一品二区三区| 99精品久久久久中文字幕| 国产精品露脸国语对白| 精品欧洲av无码一区二区三区| 久久精品国产亚洲av日韩| 日韩人妻精品一区二区三区视频 | 国产精品欧美日韩| 久久免费国产精品一区二区 | 99在线精品视频| 欧美精品一本久久男人的天堂| 久久se精品一区二区|