Heim > Artikel > Web-Frontend > Wie geht das „Multithreading“ von node.js mit Aufgaben mit hoher Parallelität um?
Der folgende Artikel zeigt Ihnen, wie Sie nodejs „Multi-Threading“ verwenden, um Aufgaben mit hoher Parallelität zu bewältigen. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird für alle hilfreich sein.
Verwandte Empfehlungen: „nodejs Video-Tutorial“
Moores Gesetz wurde 1965 von Intel-Mitbegründer Gordon Moore vorgeschlagen, d. h. die Anzahl der integrierten Schaltkreise, die sein können Die Anzahl der untergebrachten Komponenten verdoppelt sich alle 18 bis 24 Monate und die Leistung verdoppelt sich. Das heißt, die Leistung des Prozessors (CPU) verdoppelt sich alle etwa zwei Jahre.
Mehr als 50 Jahre sind vergangen, seit Moores Gesetz vorgeschlagen wurde. Heutzutage wird es immer schwieriger, mit dem Mooreschen Gesetz Schritt zu halten, da Chipkomponenten immer näher an die Größe eines einzelnen Atoms heranrücken.
Im Jahr 2019 sagte Jensen Huang, CEO von NVIDIA, auf der ECS-Ausstellung: „Früher wuchs das Mooresche Gesetz alle 5 Jahre um das Zehnfache und alle 10 Jahre um das 100-fache. Aber jetzt kann das Mooresche Gesetz jedes Jahr nur noch um ein paar Prozentpunkte wachsen.“ 10 Jahre.“ Vielleicht nur 2 Mal. Daher ist das Mooresche Gesetz vorbei. „
Die Leistung eines einzelnen Prozessors (CPU) nähert sich dem Engpass. Wenn Sie diesen Engpass durchbrechen möchten, müssen Sie ihn voll ausnutzen der Multi-Threading-Technologie
ermöglicht einer einzelnen oder mehreren CPU
die gleichzeitige Ausführung mehrerer Threads, um Computeraufgaben schneller abzuschließen. 多线程技术
,让单个或多个 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
Nodes Multithreading
Wir alle wissen, dassJavascript
eine Single-Threaded-Sprache ist, Nodejs
nutzt die Funktionen von Javascript
und verwendet ereignisgesteuert Das Modell implementiert asynchrone E/A, und hinter asynchroner E/A steht Multithread-Planung.
Node
Für die Implementierung asynchroner E/A können Sie sich auf Pu Lings „Node.js in einfachen Worten“ beziehen
Go
In dieser Sprache können Sie Goroutine
erstellen, um explizit einen neuen Thread aufzurufen, und die maximale Anzahl von Parallelitäten über die Umgebungsvariable GOMAXPROCS
steuern. 🎜In Node
gibt es keine API
, die explizit einen neuen Thread erstellen kann. Node
implementiert einige asynchrone E/A-APIs, wie z code>fs.readFile, http.request
. Die unterste Ebene dieser asynchronen E/A besteht darin, neue Threads aufzurufen, um asynchrone Aufgaben auszuführen, und dann das ereignisgesteuerte Modell zu verwenden, um die Ausführungsergebnisse zu erhalten. 🎜🎜Serverseitige Entwicklung und Toolentwicklung erfordern möglicherweise die Verwendung einer Multithread-Entwicklung. Verwenden Sie beispielsweise Multithreading, um komplexe Crawler-Aufgaben zu verarbeiten, Multithreading, um gleichzeitige Anforderungen zu verarbeiten, Multithreading für die Dateiverarbeitung usw. 🎜🎜Wenn wir Multithreading verwenden, müssen wir die maximale Anzahl steuern gleichzeitige Parallelitäten. Da die maximale Anzahl von Parallelitäten nicht kontrolliert wird, können Fehler aufgrund der Erschöpfung des Dateideskriptors
, Netzwerkfehler aufgrund unzureichender Bandbreite, Fehler aufgrund von Portbeschränkungen usw. auftreten. 🎜🎜Es gibt keine API
oder Umgebungsvariable zum Steuern der maximalen Anzahl von Parallelitäten in Node
, daher werden wir als Nächstes ein paar einfache Codezeilen verwenden, um sie zu implementieren. 🎜🎜🎜Code-Implementierung🎜🎜🎜 Nehmen wir zunächst das folgende Bedarfsszenario an: Ich habe einen Crawler, der jeden Tag 100 Nugget-Artikel crawlen muss zu langsam. Da zu viele Netzwerkverbindungen vorhanden sind, schlagen viele Anfragen direkt fehl. 🎜🎜Dann können wir es umsetzen, jedes Mal 10 Artikel anfordern und es in 10 Malen abschließen. Dies kann nicht nur die Effizienz um das Zehnfache steigern, sondern auch einen stabilen Betrieb gewährleisten. 🎜🎜Sehen wir uns eine einzelne Anforderungsaufgabe an. Der Code wird wie folgt implementiert: 🎜(async () => { const requestFnList = new Array(100) .fill("6909002738705629198") .map((id) => () => singleRequest(id)); const reply = await concurrentRun(requestFnList, 10, "请求掘金文章"); })();🎜Zur Vereinfachung fordern wir hier 100 Anforderungsaufgaben an. 🎜rrreee🎜Als nächstes implementieren wir die Methode gleichzeitiger Anforderungen. Diese Methode unterstützt die gleichzeitige Ausführung mehrerer asynchroner Aufgaben und kann die maximale Anzahl von Parallelitäten begrenzen. Nachdem eine Aufgabe im Aufgabenpool ausgeführt wurde, wird eine neue asynchrone Aufgabe gepusht, um die Ausführung fortzusetzen und eine hohe Auslastung des Aufgabenpools sicherzustellen. Der Code wird wie folgt implementiert: 🎜rrreee🎜Wie aus dem obigen Code ersichtlich ist, ist der Schlüssel zur Verwendung von
Node
für gleichzeitige Anforderungen Promise.all
, Promise .all
Mehrere asynchrone Aufgaben können gleichzeitig ausgeführt werden. 🎜🎜Im obigen Code wird ein Array mit einer Länge von max
erstellt und die entsprechende Anzahl asynchroner Aufgaben in das Array eingefügt. Verwenden Sie dann Promise.all
, um diese asynchronen Aufgaben gleichzeitig auszuführen. Wenn eine einzelne asynchrone Aufgabe abgeschlossen ist, wird eine neue asynchrone Aufgabe aus dem Aufgabenpool entfernt, um die Ausführung fortzusetzen, wodurch die Effizienz maximiert wird. 🎜🎜Als nächstes verwenden wir den folgenden Code, um den Ausführungstest durchzuführen (der Code wird wie folgt implementiert)🎜rrreee🎜Das endgültige Ausführungsergebnis ist wie in der folgenden Abbildung dargestellt: 🎜🎜🎜🎜An diesem Punkt ist unsere gleichzeitige Anfrage abgeschlossen! Als nächstes testen wir die Geschwindigkeit verschiedener Parallelitäten ~ Zuerst gibt es 1 Parallelität, also keine Parallelität (wie unten gezeigt)
Es dauerte 11,462 Sekunden! Wenn keine Parallelität verwendet wird, dauert die Aufgabe sehr lange. Sehen wir uns als Nächstes an, wie lange sie unter anderen Parallelitätsbedingungen dauert (wie unten gezeigt). Im obigen Bild wird die Geschwindigkeit der Aufgabenausführung mit zunehmender Anzahl unserer Parallelitäten immer schneller! Dies ist der Vorteil einer hohen Parallelität, die die Effizienz um ein Vielfaches oder in manchen Fällen sogar um ein Dutzendfaches verbessern kann!
Wenn wir uns den oben genannten Zeitverbrauch genauer ansehen, werden wir feststellen, dass der Zeitverbrauch mit zunehmender Anzahl von Parallelitäten immer noch einen Schwellenwert hat und nicht vollständig um ein Vielfaches ansteigen kann. Dies liegt an der (rechen-)intensiven Aufgabe.
Zu diesem Zeitpunkt haben wir die Einführung der Verwendung von Node „Multi-Threading“ zur Bewältigung von Aufgaben mit hoher Parallelität abgeschlossen. Wenn Sie möchten, dass das Programm perfekter wird, müssen Sie auch den Task-Timeout- und Fehlertoleranzmechanismus berücksichtigen. Wenn Sie interessiert sind, können Sie ihn selbst implementieren.
Einführung in die Programmierung! !
Das obige ist der detaillierte Inhalt vonWie geht das „Multithreading“ von node.js mit Aufgaben mit hoher Parallelität um?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!