다음 글에서는 nodejs "멀티스레딩"을 사용하여 동시성 작업을 처리하는 방법을 소개합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.
관련 추천: "nodejs 비디오 튜토리얼"
무어의 법칙은 1965년 인텔 공동 창업자인 고든 무어가 제안한 것, 즉 집적 회로의 수입니다. 수용되는 구성 요소의 수는 18~24개월마다 두 배로 늘어나고 성능도 두 배로 늘어납니다. 즉, 프로세서(CPU) 성능은 약 2년마다 두 배씩 증가합니다.
무어의 법칙이 제안된 지 50년 이상이 지났습니다. 오늘날 칩 구성 요소가 단일 원자 규모에 가까워짐에 따라 무어의 법칙을 따라가는 것이 점점 더 어려워지고 있습니다.
2019년 NVIDIA CEO Jensen Huang은 ECS 전시회에서 다음과 같이 말했습니다. "무어의 법칙은 5년마다 10배, 10년마다 100배씩 성장했습니다. 하지만 이제 무어의 법칙은 매년 몇 퍼센트 포인트씩만 성장할 수 있습니다. 10년." 아마도 2번 정도. 그러므로 무어의 법칙은 끝났다. "
싱글 프로세서(CPU)의 성능이 병목 현상에 가까워지고 있습니다. 이 병목 현상을 돌파하려면 최대한 활용해야 합니다. 멀티스레딩 기술
을 사용하면 단일 또는 다중 CPU
가 동시에 여러 스레드를 실행하여 컴퓨터 작업을 더 빠르게 완료할 수 있습니다. 多线程技术
,让单个或多个 CPU
可以同时执行多个线程,更快的完成计算机任务。
我们都知道,Javascript
是单线程语言,Nodejs
利用 Javascript
的特性,使用事件驱动模型,实现了异步 I/O,而异步 I/O 的背后就是多线程调度。
Node
异步 I/O 的实现可以参考朴灵的 《深入浅出 Node.js》
在 Go
语言中,可以通过创建 Goroutine
来显式调用一条新线程,并且通过环境变量 GOMAXPROCS
来控制最大并发数。
在 Node
中,没有 API
可以显式创建新线程的 ,Node
实现了一些异步 I/O 的 API,例如 fs.readFile
、http.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.all
,Promise.all
可以同时执行多个异步任务。
在上面的代码中,创建了一个长度为 max
最大并发数长度的数组,数组里放了对应数量的异步任务。然后使用 Promise.all
Node의 멀티스레딩
우리 모두는Javascript
가 단일 스레드 언어라는 것을 알고 있습니다. Nodejs
는 Javascript
의 기능을 활용합니다. > 이벤트 중심 모델을 사용합니다. 이 모델은 비동기 I/O를 구현하고 비동기 I/O 뒤에는 다중 스레드 스케줄링이 있습니다.
노드
비동기 I/O 구현에 대해서는 Pu Ling의 "간단한 용어로 Node.js"를 참조할 수 있습니다.
Go
에서 언어에서는 Goroutine
을 생성하여 새 스레드를 명시적으로 호출하고 환경 변수 GOMAXPROCS
를 통해 최대 동시성 수를 제어할 수 있습니다. 🎜노드
에는 새 스레드를 명시적으로 생성할 수 있는 API
가 없습니다. 노드
는 fs.readFile, http.request
. 이러한 비동기 I/O의 맨 아래 계층은 새 스레드를 호출하여 비동기 작업을 수행한 다음 이벤트 중심 모델을 사용하여 실행 결과를 얻는 것입니다. 🎜🎜서버측 개발 및 도구 개발에는 멀티스레드 개발을 사용해야 할 수도 있습니다. 예를 들어, 복잡한 크롤러 작업을 처리하기 위해 멀티스레딩을 사용하고, 동시 요청을 처리하기 위해 멀티스레딩을 사용하고, 파일 처리를 위해 멀티스레딩을 사용하는 등...🎜🎜멀티스레딩을 사용할 때는 최대 개수를 제어해야 합니다. 동시 동시성. 최대 동시성 수가 제어되지 않기 때문에 파일 설명자
소진으로 인한 오류, 대역폭 부족으로 인한 네트워크 오류, 포트 제한으로 인한 오류 등이 발생할 수 있습니다. 🎜🎜노드
에는 최대 동시성 수를 제어하는 API
나 환경 변수가 없으므로 다음으로 간단한 코드 몇 줄을 사용하여 구현하겠습니다. 🎜🎜🎜코드 구현🎜🎜🎜 먼저 매일 100개의 너겟 기사를 크롤링해야 하는 크롤러가 있다고 가정해 보겠습니다. 한 번에 100개의 기사를 크롤링하는 것도 너무 느릴 것입니다. 네트워크 연결이 너무 많기 때문에 많은 요청이 직접 실패합니다. 🎜🎜그러면 매번 10개의 기사를 요청하고 10번 안에 완료하는 식으로 구현할 수 있습니다. 이를 통해 효율성을 10배 높일 수 있을 뿐만 아니라 안정적인 작동도 보장합니다. 🎜🎜단일 요청 작업을 살펴보겠습니다. 코드는 다음과 같이 구현됩니다. 🎜(async () => { const requestFnList = new Array(100) .fill("6909002738705629198") .map((id) => () => singleRequest(id)); const reply = await concurrentRun(requestFnList, 10, "请求掘金文章"); })();🎜여기서는 동일한 주소를 100번 요청합니다. 코드는 다음과 같이 구현됩니다. 🎜rrreee🎜다음으로 동시 요청 방식을 구현해 보겠습니다. 이 방법은 동시에 여러 비동기 작업 실행을 지원하며 최대 동시성 수를 제한할 수 있습니다. 작업 풀의 작업이 실행된 후 작업 풀의 높은 활용도를 보장하기 위해 새로운 비동기 작업이 푸시되어 계속 실행됩니다. 코드는 다음과 같이 구현됩니다. 🎜rrreee🎜위 코드에서 볼 수 있듯이 동시 요청에
Node
를 사용하는 핵심은 Promise.all
, Promise입니다. .all
여러 비동기 작업을 동시에 실행할 수 있습니다. 🎜🎜위 코드에서는 길이가 max
인 배열이 생성되고 해당 배열에 해당 개수의 비동기 작업이 배치됩니다. 그런 다음 Promise.all
을 사용하여 이러한 비동기 작업을 동시에 실행합니다. 단일 비동기 작업이 완료되면 작업 풀에서 새로운 비동기 작업이 제거되어 실행이 극대화되어 효율성이 극대화됩니다. 🎜🎜다음으로, 다음 코드를 사용하여 실행 테스트를 수행합니다(코드는 다음과 같이 구현됩니다)🎜rrreee🎜최종 실행 결과는 아래 그림과 같습니다. 🎜🎜🎜🎜이 시점에서 동시 요청이 완료되었습니다! 다음으로 다양한 동시성 속도를 테스트해보겠습니다~ 첫 번째는 1 동시성, 즉 동시성 없음(아래 그림 참조)
11.462초가 걸렸습니다! 동시성을 사용하지 않을 경우 작업 시간이 매우 오래 걸립니다. 다음으로 다른 동시성 조건에서 시간이 얼마나 걸리는지 살펴보겠습니다(아래 참조). 위 그림에서 동시성 수가 증가함에 따라 작업 실행 속도가 점점 빨라집니다! 이것이 바로 높은 동시성의 장점으로, 효율성을 여러 번, 경우에 따라 수십 배까지 향상시킬 수 있습니다!
위의 시간 소비를 자세히 살펴보면 동시성 수가 증가함에 따라 시간 소비에는 여전히 임계값이 있으며 완전히 배수로 증가할 수는 없다는 것을 알 수 있습니다. 이는 (계산적으로) 집약적인 작업 때문입니다.
이 시점에서 우리는 높은 동시성 작업을 처리하기 위해 노드 "멀티스레딩" 사용 도입을 마쳤습니다. 프로그램을 더욱 완벽하게 만들고 싶다면 작업 시간 초과 및 내결함성 메커니즘도 고려해야 합니다. 관심이 있다면 직접 구현할 수도 있습니다.
더 많은 프로그래밍 관련 지식을 보려면프로그래밍 소개를 방문하세요! !
위 내용은 node.js '멀티스레딩'은 동시성이 높은 작업을 어떻게 처리합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!