基于Promise.resolve實現Koa請求隊列中間件

本文作者為360奇舞團前端工程師

前言

最近在做一個 AIGC 項目,后端基于 Koa2 實現。其中有一個需求就是調用兄弟業務線服務端 AIGC 能力生成圖片。但由于目前兄弟業務線的 AIGC 項目也是處于測試階段,能夠提供的服務器資源有限,當并發請求資源無法滿足時,會響應【服務器繁忙】,這樣對于 C 端展示的我們是非常不友好的。基于當前的困境,第一想到的解決方案就是KafkaRabbitMQ,但實際上對于我們目前的用戶體量來說,簡直就是大材小用。于是轉換思路,是不是可以利用js模擬隊列的方式解決問題呢,答案是:可以,PromiseResolve 隊列!

分析

Resolve 的理解

Promise 的核心用法就是利用 Resolve 函數做鏈式傳遞。例如:

new?Promise(resolve?=>?{resolve('ok')
}).then(res?=>?{console.log(res)
})
//?輸出結果:ok

通過上邊的例子我們可以理解,ResolvePromise 對象的狀態從 pending 變為 fullfilled ,在異步操作成功時調用,并將異步操作的結果,作為參數傳遞出去。

核心點:異步

此時拋出一個問題:假如我把 resolve 回調函數都放入一個隊列里,Promise 是不是一直處于pending 狀態?pending 狀態就意味著then函數一直處于 waitting 狀態,直到隊列中的 resolve 函數執行后,then 函數才能被執行?

制造阻塞的 Promise 函數

const?queue?=?[]
new?Promise(resolve?=>?{queue.push(resolve)
}).then(res?=>?{console.log(res)
})
//?輸出結果:Promise {<pending>}queue[0]('ok')
//?輸出結果:ok

為了佐證,直接貼圖:

6f6ba4680028921155a502a9f4ab0f83.png
image.png

異步轉同步

Koa2 屬于洋蔥模型,當請求過來以后需要調用 next 函數繼續穿透,而我們的需求是限流,這意味著我們要阻塞請求,此時此刻,await舉起了雙手,阻塞這種不要臉的事我在行呀!

const?queue?=?[]
const?fn?=?async?=?()?=>?{await?new?Promise(resolve?=>?{queue.push(resolve)})//?...一大波操作
}
//?queue[0]()

如果 queue[0] 不執行,代碼就會一直處于阻塞狀態。那我們就可以利用await寫一個中間件實現阻塞某些 api 的需求了。

//?阻塞所有請求,知道queue中的resolve函數被執行才會執行next
const?queue?=?[]
module.exports?=?function?()?{return?async?function?(ctx,?next)?{await?new?Promise(resolve?=>?{queue.push(resolve)})await?next();};
};

實現中間件

原理和思路都捋直了,那就開搞吧。話不多說,貼代碼:

const?resolveMap?=?{};/***?請求隊列*?@param?{*}?ctx*?@param?{*}?ifer?是否是圖生圖*?@param?{*}?maxReqNumber?最大請求數量*?@returns*?@description*?使用promise解決請求隊列問題*?1.?用于限制aicg的并發請求*?2.?當文生圖是,根據風格分類存儲resolve,當前請求響應完成時,觸發消費隊列中下一個請求*?3.?當圖生圖是,直接存儲resolve到image風格,當前請求響應完成時,觸發消費隊列中下一個請求* 4. 同時處理的請求數量不超過maxReqNumber個,否則加入隊列等待。*/
function?requestQueue(ctx,?maxReqNumber)?{const?params?=?ctx.request.body????ctx.request.query????ctx.request.params????{};const?style?=?params.style????'pruned_cgfull';resolveMap[style]?=?resolveMap[style]?||?{?list:?[],?processNumber:?0?};const?currentResolve?=?resolveMap[style];((currentResolve)?=>?{ctx.res.on('close',?()?=>?{saveNumberMinus(currentResolve);//?當前請求響應完成時,觸發消費隊列中下一個請求if?(currentResolve.list.length?!==?0)?{const?node?=?currentResolve.list.shift();node.resolve();currentResolve.processNumber++;}currentResolve?=?null;});})(currentResolve);//?當前請求正在處理中,將resolve存儲到隊列中if?(currentResolve.processNumber?+?1?>?maxReqNumber)?{//?利用promise阻塞請求return?new?Promise((resolve,?reject)?=>?{//?當前請求正在處理中,將resolve存儲到隊列中currentResolve.list.push({?resolve,?reject,?timeStamp:?Date.now(),?params?});});}?else?{currentResolve.processNumber++;return?Promise.resolve();}
}module.exports?=?function?(options?=?{})?{const?{?maxReqNumber?=?2,?apis?=?[]?}?=?options;return?async?function?(ctx,?next)?{const?url?=?ctx.url;if?(apis.includes(url))?{try?{await?requestQueue(ctx,?maxReqNumber);}?catch?(error)?{console.log(error);ctx.body?=?{code:?0,msg:?error,};return;}}await?next();};
};const?fiveMinutes?=?5?*?60?*?1000;
setInterval(()?=>?{Object.values(resolveMap).forEach((item)?=>?{const?{?timeStamp,?resolve?}?=?item;if?(Date.now()?-?timeStamp?>?fiveMinutes)?{resolve();?//?執行并釋放請求,防止用戶請求因異常積壓導致一直掛起saveNumberMinus(item);}});
},?5?*?60?*?1000);

這里要著重提示一點,閉包的使用。之所以使用閉包是為了保證當前請求的close事件觸發時能夠使用currentResolve對象。因為當前請求是放在自身對應風格的數組中,close時要消費下一個等待的請求,同時也不要忘了手動釋放資源。

app.js 邏輯部分

const?requsetQueue?=?require('./app/middleware/request-queue');
const?app?=?new?Koa();
app.use(requsetQueue({maxReqNumber:?1,apis:?['/api/aigc/image',?'/api/aigc/textToImage',?'/api/aigc/img2img'],})
);
app.listen(process.env.NODE_ENV?===?'development'???'9527'?:?'3000');

總結

其實基于 PromiseResolve 隊列,我們還可以實現一些其他的功能,比如:前端代碼中未登錄狀態下收集某些請求,等到登錄成功后發送請求。也希望大家一起探索和討論Promise的其他解決能力的實現方案。


-?END?-

關于奇舞團

奇舞團是 360 集團最大的大前端團隊,代表集團參與 W3C 和 ECMA 會員(TC39)工作。奇舞團非常重視人才培養,有工程師、講師、翻譯官、業務接口人、團隊 Leader 等多種發展方向供員工選擇,并輔以提供相應的技術力、專業力、通用力、領導力等培訓課程。奇舞團以開放和求賢的心態歡迎各種優秀人才關注和加入奇舞團。

0c94266f4114bc66f653a577a9889d38.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/37638.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/37638.shtml
英文地址,請注明出處:http://en.pswp.cn/news/37638.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

kafka和rabbitmq之間的區別以及適用場景

Kafka 和 RabbitMQ 都是流行的消息傳遞系統&#xff0c;用于實現分布式系統中的消息傳遞、事件處理和數據流。它們在設計和適用場景上有一些不同&#xff0c;下面詳細介紹它們之間的區別和適用場景。 Kafka 特點和優勢&#xff1a; 高吞吐量&#xff1a; Kafka 的設計目標是實…

【Java】數據交換 Json 和 異步請求 Ajax

&#x1f384;歡迎來到邊境矢夢的csdn博文&#xff0c;本文主要講解Java 中 數據交換和異步請求 Json&Ajax 的相關知識&#x1f384; &#x1f308;我是邊境矢夢&#xff0c;一個正在為秋招和算法競賽做準備的學生&#x1f308; &#x1f386;喜歡的朋友可以關注一下&#…

go mod 添加私有庫GOPRIVATE

私有地址 形式倉庫域名/組織名形式倉庫域名形式*倉庫域名 示例私有地址&#xff1a; gitee.com/takujo_admin 或者igitlab.com 多個私有地址,分割&#xff0c;示例&#xff1a; gitee.com,igitlab.com 修改env go env -w GOPRIVATE"私有地址" go env -w …

conda創建虛擬環境

創建虛擬環境是在計算機上設置一個獨立的空間&#xff0c;用于安裝和運行特定版本的軟件和依賴項&#xff0c;以避免與系統其他部分的沖突。 創建虛擬環境&#xff1a; conda create --name myenv python3.8 這將創建一個名為myenv的虛擬環境&#xff0c;并安裝Python 3.8版本。…

pwm接喇叭搞整點報時[keyestudio的8002模塊]

雖然現在查看時間很方便&#xff0c;但是其實好像我的時間觀念卻越來越差。于是決定搞一個整點報時&#xff0c;時常提醒自己時光飛逝&#xff0c;不要老是瞎墨跡。 這篇主要講一下拼裝方式和配置&#xff0c;就差不多了。不涉及什么代碼。3針的元器件&#xff0c;去掉正負接線…

day3 STM32 GPIO口介紹

GPIO接口簡介 通用輸入輸出接口GPIO是嵌入式系統、單片機開發過程最常用的接口&#xff0c;用戶可以通過編程靈活的對接口進行控制&#xff0c;實現對電路板上LED、數碼管、按鍵等常用設備控制驅動&#xff0c;也可以作為串口的數據收發管腳&#xff0c;或AD的接口等復用功能使…

網絡安全--iptables(待更新,累了)

總結&#xff1a; iptables 的關鍵概念和功能&#xff1a; 規則&#xff08;Rules&#xff09;&#xff1a; iptables 使用規則來定義特定的操作&#xff0c;例如允許或拒絕特定類型的網絡流量。每條規則都由條件和操作組成。條件可以是源 IP 地址、目標 IP 地址、端口號等&a…

thinkphp:對數據庫減少增加某個字段的值(dec、inc的用法)

例子&#xff1a;當字段po_num的值等于數組list_info中的po_num的值時修改數據庫表po_rcv_receipt_line中某些信息&#xff1a; 1、數據庫delivery_quantity字段的值 數據庫中delivery_quantity的值變量$list_info[write_quantity] ->inc(delivery_quantity, $list_info[…

【設計模式——學習筆記】23種設計模式——狀態模式State(原理講解+應用場景介紹+案例介紹+Java代碼實現)

文章目錄 案例引入介紹基本介紹登場角色應用場景 案例實現案例一類圖實現 案例二&#xff1a;借貸平臺源碼剖析傳統方式實現分析狀態修改流程類圖實現 案例三&#xff1a;金庫警報系統系統的運行邏輯偽代碼傳統實現方式使用狀態模式 類圖實現分析問題問題一問題二 總結文章說明…

國內芯片廠商創新突破,助力國產替代持續加速

近日&#xff0c;中商產業研究院發布最新研究報告顯示&#xff0c;今年1~5月份中國進口集成電路為1865億件&#xff0c;同比下降19.6%&#xff0c;同比去年5個月累計少進口了455億顆&#xff0c;平均每天少進口3億顆。與此同時&#xff0c;英特爾、AMD、美光、三星、SK海力士等…

OSI七層模型和TCP/IP四層模型

OSI七層模型和TCP/IP四層模型 七層模型(OSI) OSI七層模型&#xff08;Open Systems Interconnection Reference Model&#xff09;是一個用于計算機網絡體系結構的標準化框架&#xff0c;旨在定義網絡通信中不同層次的功能和協議。 各個層次具體如下&#xff1a; 物理層&am…

C語言 冒泡排序

目錄 一、原理 二、代碼演示 三、代碼優化 一、原理 假設&#xff1a; int arr[] { 9,8,7,6,5,4,3,2,1,0 }; 將 arr 內的元素進行升序排列&#xff0c;得到一個新的數組 int arr[] { 0&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;…

windows docker mysql8.0 掛載配置文件不生效的問題

原因 mysql 8.0 遇到sql_modeonly_full_group_by的問題&#xff0c;于是就自定義my.cnf 去掉only_full_group_by&#xff0c;修改my.cnf 文件后&#xff0c;進行映射啟動 docker run 命令 docker run -p 3306:3306 --privilegedtrue --restartalways -d --name axsc-mysql -…

【0814作業】多線程并發服務器

1) 代碼 #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <sys/wait.h> #include <netinet/in.h>…

配置文件優先級解讀

目錄 概述 同級目錄application配置文件優先級 application 以及bootstrap 優先級 不同級目錄配置文件優先級 外部配置加載順序 概述 SpringBoot除了支持properties格式的配置文件&#xff0c;還支持另外兩種格式的配置文件。三種配置文件格式分別如下: properties格式…

Python學習筆記_基礎篇(二)_數據類型之字符串

一.基本數據類型 整數&#xff1a;int 字符串&#xff1a;str(注&#xff1a;\t等于一個tab鍵) 布爾值&#xff1a; bool 列表&#xff1a;list 列表用[] 元祖&#xff1a;tuple 元祖用&#xff08;&#xff09; 字典&#xff1a;dict 注&#xff1a;所有的數據類型都存在想對應…

TFTP Server

簡介 TFTP&#xff08;Trivial File Transfer Protocol,簡單文件傳輸協議&#xff09;是TCP/IP協議族中的一個用來在客戶機與服務器之間進行簡單文件傳輸的協議&#xff0c;提供不復雜、開銷不大的文件傳輸服務。端口號為69。 TFTP和FTP的區別 安全性區別 FTP支持登錄安全&…

ATF(TF-A)安全通告 TFV-9 (CVE-2022-23960)

ATF(TF-A)安全通告匯總 目錄 一、ATF(TF-A)安全通告 TFV-9 (CVE-2022-23960) 二、CVE-2022-23960 一、ATF(TF-A)安全通告 TFV-9 (CVE-2022-23960) Title TF-A披露通過分支預測目標重用&#xff08;branch prediction target reuse&#xff09;引發的前瞻執行處理器漏洞 CV…

Softmax Strategy

1. epsilon-greedy strategy 11111 2. UCB strategy 222 3. Softmax strategy 333 4. Gradient strategy 444 References [1] 科學網—【RL系列】Multi-Armed Bandit筆記——Softmax選擇策略 - 管金昱的博文 [2] The Epsilon-Greedy Algorithm | James D. McCaffrey

【軟考】2023系統架構設計師考試

目錄 1 軟考資格設置 2 考試報名 3 考試準備 4 參加考試 5 考試感受 6 其他 1 軟考資格設置 2 考試報名 報名網址&#xff1a;https://www.ruankao.org.cn/ 3 考試準備 4 參加考試 2023年下半年系統架構設計師考試時間為11月4、5日。 5 考試感受 6 其他 最近好像有地區…