如何DRY地扩展Node.js的http.Agent与https.Agent并复用含super的方法?
解决方案:用高阶函数封装通用逻辑
要给http.Agent和它的子类https.Agent同时添加并发限制逻辑,又要符合DRY原则,最合理的方式是用高阶函数封装通用的并发限制代码,动态生成继承自目标Agent类的子类。这样既不用重复编写逻辑,又能让super正确指向原Agent类的方法。
实现步骤
1. 编写高阶生成函数
这个函数接收一个Agent基类(可以是http.Agent或https.Agent),返回一个带有并发限制逻辑的子类:
function createConcurrencyLimitedAgent(BaseAgent) { return class ConcurrencyLimitedAgent extends BaseAgent { constructor(options = {}) { super(options); // 存储每个目标(host:port)的当前并发请求数 this._activeRequests = new Map(); // 默认并发上限,可通过options传入 this.maxConcurrent = options.maxConcurrent || 10; // 监听socket释放事件,减少对应目标的并发计数 this.on('free', (socket) => { const targetKey = `${socket.remoteAddress}:${socket.remotePort}`; const currentCount = this._activeRequests.get(targetKey); if (currentCount && currentCount > 0) { this._activeRequests.set(targetKey, currentCount - 1); } }); } addRequest(req, options) { // 生成目标唯一标识(用host+port区分) const targetKey = `${options.host}:${options.port}`; const currentCount = this._activeRequests.get(targetKey) || 0; if (currentCount < this.maxConcurrent) { // 更新并发计数 this._activeRequests.set(targetKey, currentCount + 1); // 调用原Agent的addRequest方法 super.addRequest(req, options); } else { // 无容量时直接触发请求错误,避免排队 req.emit('error', new Error(`Reached max concurrent requests for ${targetKey}`)); } } }; }
2. 生成带限制的Agent类
针对http.Agent和https.Agent分别生成子类:
const http = require('http'); const https = require('https'); // 生成http版本的并发限制Agent const LimitedHttpAgent = createConcurrencyLimitedAgent(http.Agent); // 生成https版本的并发限制Agent const LimitedHttpsAgent = createConcurrencyLimitedAgent(https.Agent);
3. 使用示例
// 初始化http代理,设置最大并发为5 const httpAgent = new LimitedHttpAgent({ maxConcurrent: 5 }); // 初始化https代理,设置最大并发为5 const httpsAgent = new LimitedHttpsAgent({ maxConcurrent: 5 }); // 发起http请求时使用代理 http.get('http://example.com', { agent: httpAgent }, (res) => { // 处理响应 }).on('error', (err) => { console.error('请求被拒绝:', err.message); }); // 发起https请求时使用代理 https.get('https://example.com', { agent: httpsAgent }, (res) => { // 处理响应 }).on('error', (err) => { console.error('请求被拒绝:', err.message); });
方案优势
- 完全符合DRY原则:并发限制的核心逻辑只编写一次,复用所有Agent子类
super调用正常工作:生成的子类直接继承自目标Agent类,super.addRequest会正确调用原类的方法- 保留原类特性:生成的LimitedAgent仍然是原Agent的子类,继承所有原生属性和方法
- 灵活配置:可以通过构造函数参数自定义并发上限
内容的提问来源于stack exchange,提问作者user3280862
相关产品推荐
相关产品推荐

