目录
摩尔定律
Node 的多线程
代码实现
首页 web前端 js教程 node.js“多线程”如何处理高并发任务?

node.js“多线程”如何处理高并发任务?

Dec 29, 2020 pm 06:35 PM
javascript node.js 前端 多线程

下面本篇文章给大家介绍一下使用 nodejs “多线程”处理高并发任务的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

node.js“多线程”如何处理高并发任务?

相关推荐:《nodejs视频教程

摩尔定律

摩尔定律是由英特尔联合创始人戈登·摩尔(Gordon Moore)在 1965 年提出的,即集成电路上可容纳的元器件的数量每隔 18 至 24 个月就会增加一倍,性能也将提升一倍。也就是说,处理器(CPU)的性能每隔大约两年就会翻一倍。

距离摩尔定律被提出到现在,已经过去了 50 多年。如今,随着芯片组件的规模越来越接近单个原子的规模,要跟上摩尔定律的步伐变得越来越困难。

在 2019 年,英伟达 CEO 黄仁勋在 ECS 展会上说:“摩尔定律过去是每 5 年增长 10 倍,每 10 年增长 100 倍。而如今,摩尔定律每年只能增长几个百分点,每 10 年可能只有 2 倍。因此,摩尔定律结束了。”

单个处理器(CPU)的性能越来越接近瓶颈,想要突破这个瓶颈,则需要充分利用 多线程技术,让单个或多个 CPU 可以同时执行多个线程,更快的完成计算机任务。

Node 的多线程

我们都知道,Javascript 是单线程语言,Nodejs 利用 Javascript 的特性,使用事件驱动模型,实现了异步 I/O,而异步 I/O 的背后就是多线程调度。

Node 异步 I/O 的实现可以参考朴灵的 《深入浅出 Node.js》

Go 语言中,可以通过创建 Goroutine 来显式调用一条新线程,并且通过环境变量 GOMAXPROCS 来控制最大并发数。

Node 中,没有 API 可以显式创建新线程的 ,Node 实现了一些异步 I/O 的 API,例如 fs.readFilehttp.request。这些异步 I/O 底层是调用了新线程执行异步任务,再利用事件驱动的模式来获取执行结果。

服务端开发、工具开发可能都会需要使用到多线程开发。比如使用多线程处理复杂的爬虫任务,用多线程来处理并发请求,使用多线程进行文件处理等等...

在我们使用多线程时,一定要控制最大同时并发数。因为不控制最大并发数,可能会导致 文件描述符 耗尽引发的错误,带宽不足引发的网络错误、端口限制引发的错误等等。

Node 中并没有用于控制最大并发数的 API 或者环境变量,所以接下来,我们就用几行简单的代码来实现。

代码实现

我们先假设下面的一个需求场景,我有一个爬虫,需要每天爬取 100 篇掘金的文章,如果一篇一篇爬取的话太慢,一次爬取 100 篇会因为网络连接数太多,导致很多请求直接失败。

那我们可以来实现一下,每次请求 10 篇,分 10 次完成。这样不仅可以把效率提升 10 倍,并且可以稳定运行。

下面来看看单个请求任务,代码实现如下:

const axios = require("axios");

async function singleRequest(article_id) {
  // 这里我们直接使用 axios 库进行请求
  const reply = await axios.post(
    "https://api.juejin.cn/content_api/v1/article/detail",
    {
      article_id,
    }
  );

  return reply.data;
}
登录后复制

为了方便演示,这里我们 100 次请求的都是同一个地址,我们来创建 100 个请求任务,代码实现如下:

// 请求任务列表
const requestFnList = new Array(100)
  .fill("6909002738705629198")
  .map((id) => () => singleRequest(id));
登录后复制

接下来,我们来实现并发请求的方法。这个方法支持同时执行多个异步任务,并且可以限制最大并发数。在任务池的一个任务执行完成后,新的异步任务会被推入继续执行,以保证任务池的高利用率。代码实现如下:

const chalk = require("chalk");
const { log } = require("console");

/**
 * 执行多个异步任务
 * @param {*} fnList 任务列表
 * @param {*} max 最大并发数限制
 * @param {*} taskName 任务名称
 */
async function concurrentRun(fnList = [], max = 5, taskName = "未命名") {
  if (!fnList.length) return;

  log(chalk.blue(`开始执行多个异步任务,最大并发数: ${max}`));
  const replyList = []; // 收集任务执行结果
  const count = fnList.length; // 总任务数量
  const startTime = new Date().getTime(); // 记录任务执行开始时间

  let current = 0;
  // 任务执行程序
  const schedule = async (index) => {
    return new Promise(async (resolve) => {
      const fn = fnList[index];
      if (!fn) return resolve();

      // 执行当前异步任务
      const reply = await fn();
      replyList[index] = reply;
      log(`${taskName} 事务进度 ${((++current / count) * 100).toFixed(2)}% `);

      // 执行完当前任务后,继续执行任务池的剩余任务
      await schedule(index + max);
      resolve();
    });
  };

  // 任务池执行程序
  const scheduleList = new Array(max)
    .fill(0)
    .map((_, index) => schedule(index));
  // 使用 Promise.all 批量执行
  const r = await Promise.all(scheduleList);

  const cost = (new Date().getTime() - startTime) / 1000;
  log(chalk.green(`执行完成,最大并发数: ${max},耗时:${cost}s`));
  return replyList;
}
登录后复制

从上面的代码可以看出,使用 Node 进行并发请求的关键就是 Promise.allPromise.all 可以同时执行多个异步任务。

在上面的代码中,创建了一个长度为 max 最大并发数长度的数组,数组里放了对应数量的异步任务。然后使用 Promise.all 同时执行这些异步任务,当单个异步任务执行完成时,会在任务池取出一个新的异步任务继续执行,完成了效率最大化。

接下来,我们用下面这段代码进行执行测试(代码实现如下)

(async () => {
  const requestFnList = new Array(100)
    .fill("6909002738705629198")
    .map((id) => () => singleRequest(id));

  const reply = await concurrentRun(requestFnList, 10, "请求掘金文章");
})();
登录后复制

最终执行结果如下图所示:

1.jpg

到这里,我们的并发请求就完成啦!接下来我们分别来测试一下不同并发的速度吧~ 首先是 1 个并发,也就是没有并发(如下图)

2.jpg

耗时 11.462 秒!当不使用并发时,任务耗时非常长,接下来我们看看在其他并发数的情况下耗时(如下图)

3.jpg

4.jpg

5.jpg

6.jpg

从上图可以看出,随着我们并发数的提高,任务执行速度越来越快!这就是高并发的优势,可以在某些情况下提升数倍乃至数十倍的效率!

我们仔细看看上面的耗时会发现,随着并发数的增加,耗时还是会有一个阈值,不能完全呈倍数增加。这是因为 Node 实际上并没有为每一个任务开一个线程进行处理,而只是为异步 I/O 任务开启了新的线程。所以,Node 比较适合处理 I/O 密集型任务,并不适合 CPU(计算)密集型任务。

到这里,我们的使用 Node “多线程”处理高并发任务就介绍完了。如果想要程序完善一点的话,还需要考虑到任务超时时间、容错机制,大家感兴趣的可以自己实现一下。

更多编程相关知识,请访问:编程入门!!

以上是node.js“多线程”如何处理高并发任务?的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

C++ 函数异常与多线程:并发环境下的错误处理 C++ 函数异常与多线程:并发环境下的错误处理 May 04, 2024 pm 04:42 PM

C++中函数异常处理对于多线程环境尤为重要,以确保线程安全和数据完整性。通过try-catch语句,可以在出现异常时捕获和处理特定类型的异常,以防止程序崩溃或数据损坏。

JUnit单元测试框架在多线程环境中的用法 JUnit单元测试框架在多线程环境中的用法 Apr 18, 2024 pm 03:12 PM

在多线程环境中使用JUnit时,有两种常见方法:单线程测试和多线程测试。单线程测试在主线程上运行,避免并发问题,而多线程测试在工作线程上运行,需要同步测试方法来确保共享资源不受干扰。常见使用案例包括测试多线程安全方法,例如使用ConcurrentHashMap存储键值对,并发线程对键值对进行操作并验证其正确性,体现了多线程环境中JUnit的应用。

Java函数的并发和多线程如何提高性能? Java函数的并发和多线程如何提高性能? Apr 26, 2024 pm 04:15 PM

使用Java函数的并发和多线程技术可以提升应用程序性能,包括以下步骤:理解并发和多线程概念。利用Java的并发和多线程库,如ExecutorService和Callable。实践多线程矩阵乘法等案例,大大缩短执行时间。享受并发和多线程带来的应用程序响应速度提升和处理效率优化等优势。

PHP 多线程如何实现? PHP 多线程如何实现? May 06, 2024 pm 09:54 PM

PHP多线程是指在一个进程中同时运行多个任务,通过创建独立运行的线程实现。PHP中可以使用Pthreads扩展模拟多线程行为,安装后可使用Thread类创建和启动线程。例如,在处理大量数据时,可将数据分割为多个块,创建对应数量的线程同时处理,提高效率。

PHP 函数在多线程环境中的行为如何? PHP 函数在多线程环境中的行为如何? Apr 16, 2024 am 10:48 AM

在多线程环境中,PHP函数的行为取决于其类型:普通函数:线程安全,可并发执行。修改全局变量的函数:不安全,需使用同步机制。文件操作函数:不安全,需使用同步机制协调访问。数据库操作函数:不安全,需使用数据库系统机制防止冲突。

C++中如何处理多线程中的共享资源? C++中如何处理多线程中的共享资源? Jun 03, 2024 am 10:28 AM

C++中使用互斥量(mutex)处理多线程共享资源:通过std::mutex创建互斥量。使用mtx.lock()获取互斥量,对共享资源进行排他访问。使用mtx.unlock()释放互斥量。

C++ 多线程程序测试的挑战和策略 C++ 多线程程序测试的挑战和策略 May 31, 2024 pm 06:34 PM

多线程程序测试面临不可重复性、并发错误、死锁和缺乏可视性等挑战。策略包括:单元测试:针对每个线程编写单元测试,验证线程行为。多线程模拟:使用模拟框架在控制线程调度的情况下测试程序。数据竞态检测:使用工具查找潜在的数据竞态,如valgrind。调试:使用调试器(如gdb)检查运行时程序状态,找到数据竞争根源。

C++ 内存管理在多线程环境中的挑战和应对措施? C++ 内存管理在多线程环境中的挑战和应对措施? Jun 05, 2024 pm 01:08 PM

在多线程环境中,C++内存管理面临以下挑战:数据竞争、死锁和内存泄漏。应对措施包括:1.使用同步机制,如互斥锁和原子变量;2.使用无锁数据结构;3.使用智能指针;4.(可选)实现垃圾回收。

See all articles