Nodejs-异步并发控制

异步并发控制

在 node 中可以利用异步发起并行调用。但是如果并发量过大,就会导致下层服务器吃不消。

bagpipe 解决方案

解决方案

  • 通过一个队列来控制并发量
  • 如果当前活跃的异步调用小于限定值,从队列中取出执行
  • 如果活跃调用达到限定值,调用暂时放到队列中
  • 每个异步调用结束的时候,从队列中取出新的异步调用执行
javascript 复制代码
var Bagpipe = require("bagpipe");
var bagpipe = new Bagpipe(10);
for (var i = 0; i < 100; i++) {
  bagpipe.push(async, function () {
    // 异步回调执行
  });
}
bagpipe.on("full", function (lenght) {
  console.warn("底层系统不能及时完成,队列拥堵");
});

核心实现

javascript 复制代码
Bagpipe.prototype.push = function (method) {
  var args = [].slice.call(arguments, 1);
  var callback = args[args.length - 1];
  if (typeof callback !== "function") {
    args.push(function () {});
  }
  if (this.options.disabled || this.limit < 1) {
    method.apply(null, args);
    return this;
  }
  if (this.queue.length < this.queueLength || !this.options.refuse) {
    this.queue.push({
      method: method,
      args: args,
    });
  } else {
    var err = new Error("too much async call in queue");
    err.name = "TooMuchAsyncCallError";
    callback(err);
  }
  if (this.queue.length > 1) {
    this.emit("full", this.queue.lenght);
  }
  this.next();
  return this;
};

next()方法主要是用来判断活跃调用的数量,如果正常,使用内部方法 run 来执行真正的调用。

javascript 复制代码
Bagpipe.prototype.next = function () {
  var that = this;
  if (that.active < that.limit && this.queue.length) {
    var req = this.queue.shift();
    this.run(req.method, req.args);
  }
};
javascript 复制代码
Bagpine.prototype.run = function (method, args) {
  var that = this;
  that.active++;
  var callback = args[args.length - 1];
  var timer = null;
  var called = false;
  args[args.length - 1] = function (err) {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    if (!called) {
      this._next();
      callback.apply(null, arguments);
    } else {
      if (err) {
        that.emit("outdated", err);
      }
    }
  };
  var timeout = that.options.timeout;
  if (timeout) {
    timer = setTimeout(function () {
      called = true;
      that._next();
      // pass the exception
      var err = new Error(timeout + "ms timeout");
      err.name = "BagpipeTimeoutError";
      err.data = {
        name: method.name,
        method: method.toString(),
        args: args.slice(0, -1),
      };
      callback(err);
    }, timeout);
  }
  method.apply(null, args);
};
  • 拒绝模式
    对于大量的异步调用,会分场景进行区分。设计到并发控制,会造成部分等待,如果调用由实时方面的需求,需要快速返回。这种情境下需要快速失败,让调用方竟早返回。
  • 超时控制
    超时控制是为异步调用设置一个时间阈值,如果异步调用没有在规定的时间内完成,先执行用户传入的回调函数。

async解决方案

paralleLimit()和parallel类似,多了一个限制并发数量的参数,使得任务只能同时并发一定数量,不是无限制并发。

javascript 复制代码
async.parallelLimit([
    function(callback) {
        fs.readFile('file1.txt','utf-8',callback)
    },
    function(callback) {
        fs.readFile('file2.txt','utf-8',callback)
    }
], 1, function(err, results) {
    // todo
})

paralleLimit()方法不能动态的增加并行任务。async提供了queue()方法来满足需求。

javascript 复制代码
var q = async.queue(function(file,callback) {
    fs.readFile(file, 'utf-8', callback);
},2)
q.drain = function () {};
fs.readdirSync('.').forEach(function (file) {
    q.push(file, function (err, data) {
    })
})

queue实现了动态添加并行任务,但是想不paralleLimit(),queue是固定的。丢失了paralleLimit()的多样性。

相关推荐
AiXed2 小时前
PC微信协议之AES-192-GCM算法
前端·数据库·python
AllData公司负责人2 小时前
实时开发平台(Streampark)--Flink SQL功能演示
大数据·前端·架构·flink·开源
小满zs2 小时前
Next.js第五章(动态路由)
前端
清沫2 小时前
VSCode debugger 调试指南
前端·javascript·visual studio code
一颗宁檬不酸3 小时前
页面布局练习
前端·html·页面布局
zhenryx3 小时前
React Native 自定义 ScrollView 滚动条:开箱即用的 IndicatorScrollView(附源码示例)
javascript·react native·react.js·typescript
金木讲编程4 小时前
Claude、Agent与Copilot协作生成Angular应用
前端·ai编程
振华OPPO4 小时前
Vue:“onMounted“ is defined but never used no-unused-vars
前端·javascript·css·vue.js·前端框架
欧雷殿5 小时前
在富阳银湖成立地域化的软件研发团队
前端·程序员·创业
狂炫冰美式5 小时前
前端实时推送 & WebSocket 面试题(2026版)
前端·http·面试