您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 Code iProcess 课程 认证 咨询 工具 火云堂 讲座吧   成长之路  
会员   
 
   
 
  
每天15篇文章
不仅获得谋生技能
更可以追随信仰
 
     
   
 订阅
  捐助
Node JS体系架构 - 单线程事件循环
 
607 次浏览     评价:  
  2018-5-11 
 
编辑推荐:
本文来自网络 ,今天让我们来了解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作为处理该请求的线程。

线程T-1读取客户端Client-1的Request-1请求,进行处理

Request-1请求不需要I/O阻塞的操作

线程T-1执行必须的操作,准备响应Response-1,并将其发送给服务端

服务端依照响应次序将Response-1回执给Client-1

Web服务端选择客户端Client-2的Request-2请求,并且从线程池中选择线程T-2作为处理该请求的线程。

线程T-2读取客户端Client-2的Request-2请求,进行处理

Request-2请求不需要I/O阻塞的操作

线程T-2执行必须的操作,准备响应Response-2,并将其发送给服务端

服务端依照响应次序将Response-2回执给Client-2

Web服务端选择客户端Client-n的Request-n请求,并且从线程池中选择线程T-n作为处理该请求的线程。

线程T-n读取客户端Client-2的Request-n请求,进行处理

Request-n请求需要较重的I/O阻塞操作和运算操作

线程T-n会花费更多的时间,和外部系统进行交互,并执行必须的操作,准备响应Response-n,并将其发送给服务端

服务端依照响应次序将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请求

检测该请求是需要I/O阻塞操作还是复杂的计算任务

如果只是简单的计算而无I/O阻塞任务,则不需要额外的线程去处理

事件循环组件执行请求中提供的所有运算(这里的运算是指JavaScript的方法)并且准备响应Response-1

事件循环组件将Response-1回执给Client-1

事件循环组件