node的事件循环是线程吗_Node.Js中事件循环,子进程和工作线程的动手指南

node的事件循环是线程吗

What makes Node.js so performant and scalable? Why is Node the technology of choice for so many companies? In this article, we will answer these questions and look at some of the advanced concepts that make Node.js unique. We will discuss:

是什么使Node.js如此高性能和可扩展性? 为什么Node是这么多公司的首选技术? 在本文中,我们将回答这些问题,并研究使Node.js变得独一无二的一些高级概念。 我们会讨论:

  1. Event Loops ➰

    事件循环➰
  2. Concurrency Models 🚈

    并发模型🚈
  3. Child Processes 🎛️

    子进程🎛️
  4. Threads and Worker Threads 🧵

    线程和工作线程🧵

JavaScript developers with a deeper understanding of Node.js reportedly earn 20-30% more than their peers. If you are looking to grow your knowledge of Node.js then this blog post is for you. Let’s dive in 🤿!!

据报道,对Node.js有更深入了解JavaScript开发人员的收入比同行高20-30%。 如果您想增加对Node.js的了解,那么此博客文章非常适合您。 让我们潜入🤿!

运行Node.js程序会怎样? (What happens when you run a Node.js Program?)

When we run our Node.js app it creates

当我们运行Node.js应用程序时,它会创建

  • 1 Process 🤖

    1过程🤖
  • 1 Thread 🧵

    1个螺纹🧵
  • 1 Event Loop ➰

    1个事件循环➰

A process is an executing program or a part of an executing program. An application can be made out of many processes. Node.js runtime, however, initiates only one process.

一个过程 是执行程序或执行程序的一部分。 一个应用程序可以由许多过程组成。 但是,Node.js运行时仅启动一个进程。

A thread is a basic unit to which the operating system allocates processor time. Think of threads as a unit that lets you use part of your processor.

一个线程 是操作系统分配处理器时间的基本单位。 将线程视为一个可以使用处理器一部分的单元。

An event loop is a continuously running loop (just like a while loop). It executes one command at a time, more on this later. For now, let’s think of it as a while loop that will run until Node has executed every line of code.

一个 事件循环是一个连续运行的循环(就像while循环一样)。 它一次执行一个命令,稍后再执行。 现在,让我们将其视为一个while循环,它将一直运行到Node执行完每一行代码为止。

Now, let’s take a look at how our code runs inside of Node.js instance.

现在,让我们看一下我们的代码如何在Node.js实例中运行。

console.log('Task 1');
console.log('Task 2');
// some time consuming for loop
for(let i = 0; i < 1000000000; i++) {
}
console.log('Task 3');

What happens when we run this code? It will first print out Task 1 then Task 2 and then it will run the time consuming for loop (we won’t see anything in the terminal for a couple seconds) and finally it will print out Task 3.

当我们运行此代码时会发生什么? 它将首先打印出Task 1然后打印Task 2 ,然后将运行耗时的循环(在终端上几秒钟不会看到任何内容),最后将打印出Task 3

Let’s look at a diagram of what’s actually happening.

让我们看一下实际发生的情况。

Node puts all our tasks into an Events queue and sends them one by one to the event loop. The event loop is single-threaded and it can only run one thing at a time. So it goes through Task 1 and Task 2 then the very big for loop and then it goes to Task 3. This is why we see a pause in the terminal after Task 2 because it is running the for a loop.

Node将我们所有的任务放入一个事件队列,并将它们一个接一个地发送到事件循环。 事件循环是单线程的,一次只能运行一件事。 因此它先经过任务1和任务2,然后经过很大的for循环,然后进入任务3。这就是为什么我们在终端中看到任务2之后暂停的原因,因为它正在运行for循环。

Now let’s do something different. Let’s replace that for loop with an I/O event.

现在让我们做些不同的事情。 让我们用一个I / O事件代替for循环。

console.log('Task 1');
console.log('Task 2');
fs.readFile('./ridiculously_large_file.txt', (err, data) => {
    if (err) throw err;
    console.log('done reading file');
    process.exit();
});
console.log('Task 3');

Pro tip: you can generate a 100mb file in linux or mac just by running this command:  dd if=/dev/urandom of=ridiculously_large_file.txt bs=1048576 count=100.

专家提示 :您只需运行以下命令即可在linux或mac中生成100mb文件: dd if=/dev/urandom of=ridiculously_large_file.txt bs=1048576 count=100

We naturally assume that this will output something similar. Just like the for loop reading big files takes time, the execution on the event loop will take some time. However, we get something totally different.

我们自然假设这将输出类似的内容。 就像for循环读取大文件需要时间一样,在事件循环上执行将花费一些时间。 但是,我们得到了完全不同的东西。

Task 1
Task 2
Task 3
done reading file

But what caused this? How did Task 3 get executed before the file was read? Well let’s take a look at the visuals below to see what’s happening.

但是是什么原因造成的呢? 在读取文件之前,如何执行任务3? 好吧,让我们看一下下面的视觉效果,看看发生了什么。

I/O tasks, network requests, and database processes are classified as blocking tasks in Node.js. So whenever the event loop encounters these tasks it sends them off to a different thread and moves on to the next task in events queue.

I / O任务,网络请求和数据库进程在Node.js中被归类为阻止任务。 因此,每当事件循环遇到这些任务时,它将它们发送到另一个线程,然后继续进行到事件队列中的下一个任务。

A thread gets initiated from the thread pool to handle each blocking task. When it is done, it puts the result in a call-back queue.

线程从线程池中启动以处理每个阻塞任务。 完成后,将结果放入回叫队列中。

When the event loop is done executing everything in the events queue it will start executing the tasks in the call-back queue. So that’s why we see done reading file at the end.

当事件循环完成后,将执行事件队列中的所有内容,它将开始执行回调队列中的任务。 这就是为什么我们看到最后done reading file的原因。

是什么使单线程事件循环模型高效? ⚙️ (What makes the Single Threaded Event Loop Model efficient? ⚙️)

JavaScript was created to do just simple things in web browsers such as form validation or simple animations. This is why it was built with a single-threaded event loop model. But running everything in one thread is considered as a disadvantage.

创建JavaScript只是为了在Web浏览器中完成简单的事情,例如表单验证或简单的动画。 这就是为什么它是用单线程事件循环模型构建的。 但是将所有内容都运行在一个线程中被认为是不利的。

In 2009 Ryan Dahl, the creator of Node, saw this simple event loop model as an opportunity to build a lightweight web server.

2009年,Node的创建者Ryan Dahl将这种简单的事件循环模型视为构建轻量级Web服务器的机会。

To better understand what problem Node.js solves we should look at what typical web servers were like before Node.js came into play.

为了更好地理解Node.js解决的问题,我们应该看看Node.js投入使用之前典型的Web服务器是什么样的。

This is how a traditional multi-threaded web application model handles a request:

这是传统的多线程Web应用程序模型如何处理请求的方式:

  1. It maintains a thread pool (a collection of available threads)

    它维护一个线程池(可用线程的集合)
  2. When client request comes in, a thread is assigned

    客户端请求进入时,将分配一个线程
  3. This thread will take care of reading client requests, processing client requests, performing any blocking IO operations (if required) and preparing a response.

    该线程将负责读取客户端请求,处理客户端请求,执行任何阻塞的IO操作(如果需要)并准备响应。
  4. This thread is not free until a response is sent back

    在发送回响应之前,该线程不是空闲的

The main drawback of this model is handling concurrent users. So let’s say if we have more users visiting our site than there are available threads then some users will need to wait until a thread frees up to get response.

该模型的主要缺点是处理并发用户。 假设我们访问网站的用户多于可用线程,那么一些用户将需要等到线程释放以获取响应。

If a lot of users are performing blocking I/O tasks then this wait time also increases. This is also very resource-heavy – so if we are expecting one million concurrent users we better make sure we have enough threads to handle those requests.

如果许多用户正在执行阻止I / O任务,则此等待时间也会增加。 这也是非常重的资源-因此,如果我们期望有100万并发用户,则最好确保我们有足够的线程来处理这些请求。

Moreover, the server itself start to slow down because of the increasing load. There’s also the overhead of context switching between threads, and writing applications to optimize threads' resource sharing can be painful.

此外,由于负载增加,服务器本身开始变慢。 在线程之间进行上下文切换还存在开销,编写应用程序以优化线程的资源共享可能会很痛苦。

Because of the single-threaded model Node.js, it doesn’t need to spin off new threads for every single request. Node.js also delegates blocking tasks to other components as we saw earlier. Since we don’t really care about many threads this makes Node.js very lightweight and ideal for microservice-based architecture.

由于使用了单线程模型Node.js,因此不需要为每个请求都派生新线程。 如前所述,Node.js还将阻止任务委托给其他组件。 由于我们实际上并不关心很多线程,因此这使Node.js非常轻巧,非常适合基于微服务的体系结构。

节点单线程模型的缺点 (Drawbacks of Node’s Single Threaded Model )

The single-threaded event loop architecture uses resources efficiently but it does have some drawbacks.

单线程事件循环体系结构有效地利用了资源,但确实存在一些缺点。

The Node.js instance cannot immediately benefit from multiple cores in your CPU. A Java application can have immediate access to more memory as we upgrade our hardware but Node runs on a single thread.

Node.js实例无法立即受益于CPU中的多个内核。 Java 当我们升级硬件时,应用程序可以立即访问更多内存,但是Node在单个线程上运行。

This is 2020 and we are seeing more and more complicated web applications. What if our application needs to do complex computations, or run a machine learning algorithm? Or what if we want to run a complicated crypto algorithm? In this case we have to harness the power of multiple cores to increase performance.

这是2020年,我们看到越来越复杂的Web应用程序。 如果我们的应用程序需要执行复杂的计算或运行机器学习算法怎么办? 或者,如果我们想运行复杂的加密算法怎么办? 在这种情况下,我们必须利用多核的功能来提高性能。

Languages like Java and C# can programmatically initiate threads and harness the power of multiple cores. In Node.js that is not an option as we saw earlier. Node’s way of solving this problem is child_process.

诸如JavaC#之类的语言可以以编程方式启动线程并利用多核的功能。 在Node.js中 ,这不是我们前面看到的选项。 Node解决此问题的方法是child_process

节点中的子进程 (Child Process in Node)

The child_process module gives Node the ability to spawn child processes by accessing operating system commands.

child_process模块​​使Node能够通过访问操作系统命令来生成子进程。

Let’s assume we have a REST endpoint that has a long-running function and we would like to use multiple cores in our processor to execute this function.

假设我们有一个具有长期运行功能的REST端点,并且我们想在处理器中使用多个内核来执行此功能。

Here’s our code:

这是我们的代码:

const { fork } = require('child_process');
 
app.get('/endpoint', (request, response) => {
   // fork another process
   const process_ml_algo = fork('./process_data.js');
   const data = request.body.data;
   // send send the data to forked process
   process_ml_algo.send({ data });
   // listen to forked process 
   process.on('ml_algo', (result) => {
     log.info(`ml_algo executed with ${result}`);
   });
   return response.json({ status: true, sent: true });
});
// receive message from master process
process.on('ml_algo', async (message) => {
    const result = await runMachineLearningProcess(message.mails); 
 
    // send response to master process
    process.send({ result: result });
});

In the example above we demonstrate how we can spin off a new process and share data between them. Using the forked process we can take advantage of multiple cores of CPU.

在上面的示例中,我们演示了如何剥离新进程并在它们之间共享数据。 使用分叉的过程,我们可以利用CPU的多个核心。

You can take a look at all the methods of child processes in the official node docs.

您可以在官方节点docs中查看子进程的所有方法。

Here is a diagram of how child processes work

这是子进程的工作原理图

child_process is a good solution but there’s another option. The child_process module spins off new instances of Node to distribute the workload, and all these instances will each have 1 event loop 1 thread and 1 process.

child_process是一个很好的解决方案,但是还有另一个选择。 child_process模块派生出Node的新实例以分配工作量,所有这些实例将分别具有1个事件循环,1个线程和1个进程。

In 2018 Node.js introduced worker_thread. This module gives Node the ability to have:

在2018年,Node.js引入了worker_thread 。 该模块使Node具有以下功能:

  • 1 Process

    1过程
  • Multiple threads

    多线程
  • 1 Event Loop per thread

    每个线程1个事件循环

Yes! You read that right 😄.

是! 你没看错😄。

const { Worker, workerData, isMainThread, parentPort } = require('worker_threads');
 
if (isMainThread) {
  const worker1 = new Worker(__filename, { workerData: 'Worker Data 1'});
  worker1.once('message', message => console.log(message));
  const worker2 = new Worker(__filename, { workerData: 'Worker Data 2' });
  worker2.once('message', message => console.log(message));
} else {
  parentPort.postMessage('I am ' + workerData);
}

We check if it is the main thread and then create two workers and pass on messages. On the worker thread the data gets passed on through the postMessage method and the workers execute the command.

我们检查它是否为主线程,然后创建两个worker并传递消息。 在工作线程上,数据通过postMessage方法传递,工作线程执行命令。

Since worker_threads makes new threads inside the same process it requires fewer resources. Also we are able to pass data between these threads because they have the shared memory space.

由于worker_threads在同一进程内创建新线程,因此需要较少的资源。 我们也能够在这些线程之间传递数据,因为它们具有共享的内存空间。

As of January 2020 worker_threads are fully supported in the Node LST version 12. I highly recommend reading this post if you want to learn more about worker_threads.

从2020年1月开始 ,Node LST版本12中完全支持worker_threads 。如果您想了解有关worker_threads的更多信息,我强烈建议阅读此文章

结语 (Wrapping up)

And that’s it!

就是这样!

In this article we looked at how the event loop model works in Node.js, and we discussed some of the pros and cons of the single-threaded model and looked at a couple of solutions.

在本文中,我们研究了事件循环模型在Node.js中的工作方式,并讨论了单线程模型的一些优缺点,并研究了几种解决方案。

We didn’t go over all the functionalities of child_process and worker_threads. But I hope that this article provided you with a brief introduction to these concepts and why they exist. Please let me know if you have any feedback. Until next time 👋👋

我们没有讨论child_process和worker_threads的所有功能。 但我希望本文向您简要介绍了这些概念以及它们为什么存在。 如果您有任何反馈意见,请告诉我。 直到下次time

翻译自: https://www.freecodecamp.org/news/a-hands-on-guide-to-event-loop-child-process-and-worker-threads-in-node-js/

node的事件循环是线程吗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值