Node JS体系架构

来源:互联网 发布:linux avi转rmvb 编辑:程序博客网 时间:2024/06/05 18:00

原文地址:http://www.journaldev.com/7462/node-js-architecture-single-threaded-event-loop 
原作者:RAMBABU POSA

之前我们已经讨论过Node JS基础,Node JS组件和Node JS安装。今天让我们来了解Node JS的体系架构和单线程事件循环模型。

Node JS体系架构

在开始学习Node JS编程示例前,了解Node JS的体系架构是十分重要的。我们将要讨论,Node JS的底层工作原理是什么,它遵循怎样的处理模型,以及它是如何使用单线程模型处理来自客户端的并发请求。

Node JS 单线程事件循环模型

之前提到,Node JS应用使用“单线程事件循环模型”(Single Threaded Event Loop Model)处理来自客户端的多并发请求。

现在有很多的Web应用开发技术,例如JSP,Spring MVC,ASP.NET,HTML,Ajax,jQuery等。但是所有的这些技术都是遵循“多线程请求/响应”(Multi-Threaded Request-Response)的结构去处理来自客户端的多并发请求。

由于“多线程请求/响应“结构已经在大量的web应用框架中使用,因此我们应该已经对其有所了解。但是为什么Node JS却选择了与这些框架不同的体系结构呢。多线程结构和单线程事件循环结构的主要区别是什么呢。

任何一个开发者都可以轻松的学会Node JS并使用它开发程序。但是如果不了解Node JS的内部机制,则将会无法更好地设计和开发Node JS应用。因此在开发前,有必要先了解Node JS平台的内部机理。

Node JS 平台

Node JS平台使用“单线程事件循环模型”处理来自客户端的多并发请求。但是它是如何在不使用多线程的请胯下处理多并发请求呢?事件循环模型又是什么呢。我们将逐一进行讲解。

在讨论“单线程事件循环模型”之前,首先来回顾下“多线程请求-响应”结构。

传统的Web应用处理模型

当今大多数未使用Node JS进行开发的Web应用,基本上都是遵循“多线程请求-响应”结构,简称为”请求/响应模型“(Request/Response Model)

客户端将请求发送给服务端,服务端根据请求进行处理,准备响应(数据),并将其返回给客户端。

这个模型使用HTTP协议。由于HTTP是无状态协议,因此”请求/响应模型“也是无状态的模型。所以我们又可以称其为”无状态请求/响应模型“(Request/Response Stateless Model)

简而言之,这个模型使用的是多线程来处理客户端的多并发请求。在讨论其内部机理前,我们先通过下面的图进行概览。

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

  • 客户端向服务端发送请求
  • Web服务端在内部维护一个有个数限制的线程池,为客户端的请求提供服务
  • Web服务端循环监听来自客户端的请求
  • Web服务端收到请求 
    • Web服务端选择一个客户端请求
    • 从线程池(Thread pool)选择一个线程(Thread)
    • 将该线程分配给刚选择的请求
    • 该线程将会负责读取,处理客户端请求,执行任何I/O阻塞的操作(如果需要的话),以及准备响应的内容
    • 该线程将准备就绪的响应发送给Web服务端
    • Web服务端依次将响应回执给客户端

服务器采用无线循环监听客户端的请求,针对所有的客户端请求执行上述的所有步骤。这意味着对于每一个客户端请求,该模型(请求/响应模型)都要为其创建一个线程。

如果更多的客户端请求需要I/O阻塞操作的化,那么所有的线程将在准备响应阶段处于繁忙状态。这就意味着后续的客户端请求需要等待更长的时间才能得到响应。

请求/响应模型

图示描述:

  • 这里有“n“个客户端向服务端发送请求,假设它们是并发进入Web应用程序
  • 假设客户端分别为Client-1,Client-2……,Client-n
  • Web服务端维护一个有个限的线程池。假设在线程池中的线程个数是”m”
  • Web服务端依次接收客户端发送的请求 
    • Web服务端选择客户端Client-1的Request-1请求,并且从线程池中选择线程T-1作为处理该请求的线程。 
      1. 线程T-1读取客户端Client-1的Request-1请求,进行处理
      2. Request-1请求不需要I/O阻塞的操作
      3. 线程T-1执行必须的操作,准备响应Response-1,并将其发送给服务端
      4. 服务端依照响应次序将Response-1回执给Client-1
    • Web服务端选择客户端Client-2的Request-2请求,并且从线程池中选择线程T-2作为处理该请求的线程。 
      1. 线程T-2读取客户端Client-2的Request-2请求,进行处理
      2. Request-2请求不需要I/O阻塞的操作
      3. 线程T-2执行必须的操作,准备响应Response-2,并将其发送给服务端
      4. 服务端依照响应次序将Response-2回执给Client-2
    • Web服务端选择客户端Client-n的Request-n请求,并且从线程池中选择线程T-n作为处理该请求的线程。 
      1. 线程T-n读取客户端Client-2的Request-n请求,进行处理
      2. Request-n请求需要较重的I/O阻塞操作和运算操作
      3. 线程T-n会花费更多的时间,和外部系统进行交互,并执行必须的操作,准备响应Response-n,并将其发送给服务端
      4. 服务端依照响应次序将Response-n回执给Client-n

如果n>m(大多情况都是这样),也就是说需要分配给请求的线程数要大于可用的线程数。当所有的线程被使用时,那么剩余的客户端请求就在队列中等待,直到一些处于繁忙状态的线程完成对负责线程的处理任务,改变状态为空闲。

如果线程都长时间处于I/O繁忙的状态(例如,与数据库(Database),文件系统(file system),JMS队列(JMS Queue),外部服务( external services)等)。则剩余未处理的请求则将等待很长时间。


  • 当线程池的一些线程已经为执行下个任务准备就绪时,服务端将这些线程分配给剩余的客户端请求
  • 每个线程都要使用许多的资源,例如内存等。所以在线程从繁忙状态变为空闲状态时,需要释放所有占用的资源

”请求/响应模型“的缺点

  • 在处理大量增加的客户端请求时效率较差
  • 当并发的客户端请求增加时,就需要更多的线程,会导致大量的内存被占用
  • 有时候,客户端请求需要等待可用的线程来处理它们的请求
  • 浪费大量的时间处理I/O阻塞的任务

Node JS体系结构——单线程事件循环

Node JS平台不遵循”多线程无状态的请求/响应模型“,而是采用单线程事件循环模型。它的处理模型主要是基于JavaScript基本事件模型和回调函数机制的结合。

你应该对JavaScript事件和回调函数机制如何运作有了较好的了解。如果没有,请在阅读下面的内容前优先了解基本内容,以帮助理解。

由于Node JS遵循这个体系(单线程事件循环)。因此它可以轻松地处理越来越多的客户端并发请求。在讨论其内部机理前,我们先通过下面的图进行概览。

其处理的主要核心是“事件循环”(Event Loop)。如果了解了这个,那么明白它内部机制就会相对轻松一些。

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

  • 客户端向服务端发送请求
  • Node JS服务端在内部维护一个有个数限制的线程池,为客户端的请求提供服务
  • Node JS服务端循环监听来自客户端的请求,并将它们放置到一个队列中,这个队列被称为“事件队列”(Event Queue)。
  • Node JS内部存在一个叫做“事件循环”(Event Loop)的组件。它通过无限循环来接收请求并进行处理。(可以通过下面的Java伪代码加深了解)
  • 事件循环组件只使用单线程。它是Node JS平台处理模型的核心
  • 事件循环组件会检查所有在事件队列中的客户端请求。如果没有,则继续等待请求。
  • 如果存在请求,则从事件队列中提取一个请求 
    • 开始处理该请求
    • 如果该请求不需要I/O阻塞的操作,则处理必须的工作,准备响应的内容,并将准备就绪的响应发送给客户端
    • 如果该请求需要I/O阻塞的操作,如与数据库(Database),文件系统(file system),外部服务( external services)等,则使用不同的处理流程 
      • 检测内部线程池是否存在可用线程
      • 如果有,则从内部线程池提取一个线程,并将其分配处理客户端的请求
      • 线程负责读取,读取,处理客户端请求,执行任何I/O阻塞的操作,准备响应的内容,并将准备就绪的响应发送给事件循环组件
    • 该线程将会负责读取,处理客户端请求,执行任何I/O阻塞的操作(如果需要的话),以及准备响应的内容
    • 该线程将准备就绪的响应发送给Web服务端
    • 事件循环组件依次将响应回执给客户端

单线程事件循环模型

图示描述:

  • 这里有“n“个客户端向服务端发送请求,假设它们是并发进入Web应用程序
  • 假设客户端分别为Client-1,Client-2……,Client-n
  • Web服务端维护一个有个限的线程池。假设在线程池中的线程个数是”m”
  • Node JS服务端接收请求Client-1,Client-2……,Client-n,并将它们放入事件队列
  • Node JS的事件循环组件依次提取这些请求 
    • 事件循环组件选择客户端Client-1的Request-1请求 
      1. 检测该请求是需要I/O阻塞操作还是复杂的计算任务
      2. 如果只是简单的计算而无I/O阻塞任务,则不需要额外的线程去处理
      3. 事件循环组件执行请求中提供的所有运算(这里的运算是指JavaScript的方法)并且准备响应Response-1
      4. 事件循环组件将Response-1回执给Client-1
    • 事件循环组件选择客户端Client-2的Request-2请求 
      1. 检测该请求是需要I/O阻塞操作还是复杂的计算任务
      2. 如果只是简单的计算而无I/O阻塞任务,则不需要额外的线程去处理
      3. 事件循环组件执行请求中提供的所有运算(这里的运算是指JavaScript的方法)并且准备响应Response-2
      4. 事件循环组件将Response-2回执给Client-2
    • 事件循环组件选择客户端Client-n的Request-n请求 
      1. 检测该请求是需要I/O阻塞操作还是复杂的计算任务
      2. 由于该请求是复杂的运算或I/O阻塞任务,因此事件循环组件不去处理
      3. 事件循环组件从内部线程池提取线程T-1,并分配其去处理请求Request-n的任务
      4. 线程T-1处理请求Request-n,执行必要的I/O阻塞和计算任务,并准备响应Response-n
      5. 线程T-1将响应Response-n回执给事件循环组件
      6. 事件循环组件按照次序将Response-n回执给Client-n

这里的客户端请求是一个或多个JavaScript函数。该函数可以调用其他函数或是使用回调函数(Callback function

每个客户端请求格式如下所示:

function(other-functioncall, callback-function)

例如:

function1(function2,callback1);function2(function3,callback2);function3(input-params);
  • 1
  • 2
  • 3

注意:

  1. 如果你不理解这些函数是如何执行的,那么可能你对JavaScript的函数和回调机制不够熟悉
  2. 我们应该对JavaScript函数和回调机制有所了解。因此在开始开发Node JS应用前请先通过网上的相关教程进行学习

Node JS体系结构——单线程事件循环的优点

  1. 能够轻松处理越来越多的客户端请求
  2. 因为有事件循环组件,因此即使Node JS应用接收到越来越多的客户端并发请求时,也无需创建更多的线程
  3. 由于使用较少的线程,因此可以减少资源和内存的使用

事件循环的伪代码

由于我是一个Java开发人员,我尝试使用Java术语解释“事件循环是如何工作的”。这并非是纯正的Java代码,我猜每个人都可以明白。如果你在理解上有任何问题,请在评论中告诉我。

public class EventLoop {while(true){            if(Event Queue receives a JavaScript Function Call){                ClientRequest request = EventQueue.getClientRequest();                            If(request requires BlokingIO or takes more computation time)                                    Assign request to Thread T1                            Else                                  Process and Prepare response                  }            }} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这就是关于Node JS体系结构和单线程事件循环的全部内容。

原创粉丝点击