NodeJS架构 - 单线程事件循环模型

Event Loop

这篇译章探究了NodeJS的架构和单线程事件循环模型。咱们将在本文中讨论“NodeJS如何在底层工做,它遵循什么类型的处理模型,NodeJS如何使用单线程模型处理并发请求”等内容。node

NodeJS 单线程事件循环模型

正如咱们刚才说的,NodeJS使用的是“单线程事件循环模型”的架构去处理多个并发的客户端请求的。数据库

有许多Web应用程序技术,如JSP,Spring MVC,ASP.NET等。但全部这些技术都遵循“多线程请求 - 响应”架构来处理多个并发客户端。服务器

咱们已经熟悉“多线程请求 - 响应”架构,由于它被大多数Web应用程序框架使用。 可是为何NodeJS选择了不一样的架构来开发Web应用程序。多线程和单线程事件循环体系结构之间的主要区别是什么?多线程

NodeJS

NodeJS使用“单线程事件循环模型”架构来处理多个并发客户端。然而它是如何真正处理并发客户端请求且不使用多个线程。什么是事件循环模型?咱们将逐一讨论这些概念。架构

在讨论“单线程事件循环”架构以前,首先咱们将介绍著名的“多线程请求 - 响应”架构。并发

传统的Web应用处理模型

任何非NodeJS开发的Web应用程序一般都遵循“多线程请求 - 响应”模型。咱们能够将此模型称为请求/响应模型。框架

客户端向服务器发送请求,而后服务器根据客户端请求进行一些处理,准备响应并将其发送回客户端。函数

该模型使用HTTP协议。因为HTTP是无状态协议,所以该请求/响应模型也是无状态模型。因此咱们能够将其称为请求/响应无状态模型。oop

可是,此模型使用多线程来处理并发客户端请求。 在讨论这个模型内部以前,首先要看下面的内容。spa

请求/响应模型处理的步骤:

  • 客户端发送一个请求到Web服务器
  • Web服务器内部维护一个有限的线程池,以便在客户端请求提供服务
  • Web服务器处于无限循环中并等待客户端传入请求
  • Web服务器处理请求步骤:

    • 接收到一个客户端请求
    • 从线程池中选择一个线程
    • 将此线程分配给客户端请求
    • 此线程读取客户端请求,处理客户端请求,执行阻塞的IO操做(若是须要)和准备响应
    • 此线程将准备好的请求发送回Web服务器
    • Web服务器又将此响应发送到相应的服务器

服务器为全部客户端执行以上步骤,为每个客户端请求建立一个线程。

请求/响应模型

图表说明:

  • Client-1, Client-2, ..., Client-n是同时发送请求到Web服务器的客户端应用
  • Web服务器内部维护着一个有限的线程池,线程池中线程数量为m个
  • Web服务器逐个接收这些请求:

    • Web服务器拾取Client-1的请求Request-1,从线程池中拾取一个线程T-1并将此请求分配给线程T-1

      • 线程T-1读取Client-1的请求Request-1, 并处理该请求
      • 该请求无阻塞IO处理
      • 处理完必要的步骤后准备将Response-1发送回客户端
      • Web服务器又将此Response-1发送到Client-1
    • Web服务器拾取Client-2的请求Request-2,从线程池中拾取一个线程T-2并将此请求分配给线程T-2

      • 线程T-2读取Client-2的请求Request-2, 并处理该请求
      • 该请求无阻塞IO处理
      • 处理完必要的步骤后准备将Response-2发送回客户端
      • Web服务器又将此Response-2发送到Client-2
    • Web服务器拾取Client-n的请求Request-n,从线程池中拾取一个线程T-n并将此请求分配给线程T-n

      • 线程T-n读取Client-n的请求Request-n, 并处理该请求
      • Request-n须要大量的阻塞IO和计算操做
      • 线程T-n须要更多时间与外部系统(SQL, File System)交互,执行必要步骤并准备Response-n并将其发送回服务器
      • Web服务器又将此Response-n发送到Client-n

若是'n'大于'm'(大多数时候,它是真的),则在使用完全部的m个线程以后,剩余的客户端请求会在队列中等待。

若是这些线程中有大量的阻塞IO操做(例如:和数据库、文件系统、外部服务等交互),那么剩余的客户端也会等待更长的时间。

  • 一旦线程池中的线程空闲且可用于下一个任务,服务器就会拾取这些线程并将它们分配给剩余的客户端请求。
  • 每一个线程都会使用到许多资源,如内存等。所以,在将这些线程从忙状态转到等待状态以前,它们应该释放全部获取的资源。

请求/响应无状态模型的缺点:

  • 在处理愈来愈多的并发客户端请求时会变得棘手
  • 当客户端请求增长时,线程也会愈来愈多,最后它们会占用更多内存。
  • 客户端可能须要等待服务器释放可用的线程去处理其请求
  • 处理阻塞式的IO任务时浪费时间

NodeJS的架构 - 单线程事件循环

NodeJS不遵循请求/响应多线程无状态模型。 它采用单线程与事件循环模型。 NodeJS的处理模型主要基于Javascript基于事件的模型和Javascript回调机制。

由于NodeJS遵循的架构,它能够很是轻松地处理愈来愈多的并发客户端请求。 在讨论这个模型内部以前,首先要看下面的图表。

我试图设计这个图来解释NodeJS内部的每一点。

NodeJS的处理模型主要核心是“事件循环(Event Loop)”。若是咱们理解这一点,那么很容易理解NodeJS的内部架构的。

单线程事件循环模型的处理步骤

  • 客户端发送请求到Web服务器
  • NodeJS的Web服务器在内部维护一个有限的线程池,以便为客户端请求提供服务
  • NodeJS的Web服务器接收这些请求并将它们放入队列中。 它被称为“事件队列”
  • NodeJS的Web服务器内部有一个组件,称为“事件循环”,它使用无限循环来接收请求并处理它们。
  • 事件循环只使用到了一个线程,它是NodeJS的处理模型的核心
  • 事件循环回去检查是否有客户端的请求被放置在事件队列中。若是没有,会一直等待事件队列中存在请求。
  • 若是有,则会从事件队列中拾取一个客户端请求:

    • 开始处理客户端请求
    • 若是该客户端请求不须要任何阻塞IO操做,则处理全部内容,准备响应并将其发送回客户端
    • 若是该客户端请求须要一些阻塞IO操做,例如与数据库,文件系统,外部服务交互,那么它将遵循不一样的方法:

      • 从内部线程池检查线程可用性
      • 获取一个线程并将此客户端请求分配给该线程
      • 该线程负责接收该请求,处理该请求,执行阻塞IO操做,准备响应并将其发送回事件循环
      • 事件循环依次将响应发送到相应的客户端

单线程事件循环模型

图表说明:

  • Client-1, Client-2, ..., Client-n是同时发送请求到Web服务器的客户端应用
  • Web服务器内部维护着一个有限的线程池,线程池中线程数量为m个
  • NodeJS的Web服务器接收到Client-1, Client-2, ..., Client-n的请求后,将请求放入到事件队列中
  • NodeJS的事件循环从队列中开始拾取这些请求:

    • 事件循环拾取Client-1的请求Request-1

      • 检查Client-1 Request-1是否确实须要任何阻塞IO操做,或者须要更多时间来执行复杂的计算任务
      • 因为此请求是简单计算和非阻塞IO任务,所以不须要单独的线程来处理它
      • 事件循环处理该请求所须要的操做,准备其响应Response-1
      • 事件循环发送Response-1到Client-1
    • 事件循环拾取Client-2的请求Request-2

      • 检查Client-2 Request-2是否须要任何阻塞IO操做或花费更多时间来执行复杂的计算任务
      • 因为此请求是简单计算和非阻塞IO任务,所以不须要单独的线程来处理它
      • 事件循环处理该请求所须要的操做,准备其响应Response-2
      • 事件循环发送Response-2到Client-2
    • 事件循环拾取Client-n的请求Request-n

      • 检查Client-n Request-n是否须要任何阻塞IO操做或花费更多时间来执行复杂的计算任务
      • 因为此请求有很是复杂的计算或阻塞IO任务,所以事件循环不会处理此请求
      • 事件循环从内部线程池中获取线程T-1,并将此Client-n Request-n分配给线程T-1
      • 线程T-1读取并处理Request-n,执行必要的阻塞IO或计算任务,最后准备响应Response-n
      • 线程T-1将此Response-n发送到事件循环

事件循环依次将此Response-n发送到Client-n

此处客户端请求是对一个或多个JavaScript函数的调用,由于JavaScript函数能够调用其余函数或能够利用其回调函数性质。

此因此每一个客户端的请求处理都看起来向这样:

回调函数调用

例如:

function1(function2,callback1);
function2(function3,callback2);
function3(input-params);

NodeJS的单线程事件循环的优点

  • 处理愈来愈多的并发客户端请求很是容易
  • 由于事件循环的存在,即便咱们的NodeJS应用接收到了愈来愈多的并发请求,咱们也不须要去新建不少的线程
  • NodeJS使用到了较少的线程,因此资源和内存的使用较少


原文地址: NodeJS Architecture – Single Threaded Event Loop
相关文章
相关标签/搜索