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()的多样性。

相关推荐
90后的晨仔7 小时前
从 H5 到 uni-app:一篇写给前端小白的"翻译指南"
前端·vue.js·前端框架
陈随易8 小时前
moon,apt和yum之外linux系统命令安装新选择
前端·后端·程序员
IT小盘8 小时前
13-企业Prompt模板-角色任务约束与输出格式
java·前端·prompt
徐小夕9 小时前
开源!我用SQLite + DuckDB打造了一款可视化AI问数平台
前端·算法·github
leslie1189 小时前
babel笔记
前端
用户0595401744610 小时前
Redis 记忆存储踩坑实录:一个并发写入 Bug 让我排查了 4 小时
前端·css
小徐_233310 小时前
Wot UI 2.3.0 发布:二维码组件来了,Open Wot 与 wot-starter 同步更新
前端·微信小程序·uni-app
kyriewen11 小时前
Claude自己跑出去hack了3家公司——我为什么还在用它写代码
前端·ai编程·claude
IT_陈寒11 小时前
SpringBoot自动配置的坑,这次真踩疼我了
前端·人工智能·后端
子兮曰11 小时前
AI 浏览器 Agent 的安全困局:当自动化工具可以偷走你的登录态
前端·人工智能·后端