单线程架构的Node.js如何实现异步操作?

Posted BitTiger

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单线程架构的Node.js如何实现异步操作?相关的知识,希望对你有一定的参考价值。

本文节选自BitTiger CS503 全栈开发实战精品课,5月18日正式开课!报名最后五天截止!点击阅读原文马上预约咨询!

 

Node.js有一个最重要的概念就是单线程异步IO。也许你心中也曾嘀咕过很疑惑,如果是单线程那怎么实现异步操作,单线程谁去响应事件?今天我们就来谈谈这一点,解开这个美丽的误会。

 

首先明确一点虽然我们在学习操作系统和考试的时候是区分线程和进程的,但在很多情况下我们可以把它们当作一回事看待。我们的操作系统很明显是支持多进程的,比如我用虾米音乐听着歌的同时,写下了这篇文章,后台还挂着微信和QQ。

 

经过多年发展,我们使用的CPU发展到了多核,但在最初的时候,CPU都是单核的,所以如果要支持多进程,就必须采用CPU分时(Time Sharing)技术,每个进程都轮流执行一定时间,做到“雨露均沾”。当然,由于CPU执行速度快和每个分时片段时间极短,让用户觉得好像每个进程都是一直在执行的。

 

如下图所示,很多传统Web服务器沿袭了操作系统的设计模式,也都是支持多线程(多进程)。



虽然实现了多线程的支持,但又出现了新的问题——上下文切换耗时。由于执行时间片段有限,每个进程可能到时间到了以后还没有执行完,但又必须把CPU使用权限交出去,那就只有把中间结果保存到寄存器中,直到下一次轮到又要从寄存器中读取出中间结果,反复如此。而我们也知道这种读写操作相对于CPU计算,速度是差了好几个数量级的,像这样就把大量时间花在I/O上很划不来。

 

那我们想一下操作系统由于它的特性,必须支持多线程,但一般的Web服务器并不是这样。对于Web服务器而言,任何请求都可以按照先来后到的原则进行处理。所以把Web服务器设计成单线程也并没有什么问题。

 

把每个请求处理完了再处理下一个,理论上可行,但事实上有很大的问题。每个请求除了CPU计算,还可能有数据库的读写,网络传输,文件读写等都是耗费时间的I/O操作。当你用单线程在进行“缓慢”的I/O操作时候,CPU就“无所事事”了。

 

这个问题在多线程中就不存在了,当轮到某个线程执行的时候发现还在I/O操作,就可以暂时跳过这个线程,去下一个线程。这样CPU一直不停运作。

 

这么看来,好像还是多线程的模式好一些。其实不然,多线程还有一个大问题,就在于线程池的管理,假设现在有100万个请求,其中80万个有I/O操作,轮询到这些线程的时候都会暂时跳过处理,那线程池里面就会有80万个线程存在。管理如此大量的线程,就会变成一个头疼的问题。

 

针对多线程上下文切换和庞大线程池管理两个问题以及单线程会出现CPU闲置的问题,Node.js提出了一套方案。


单线程架构的Node.js如何实现异步操作?


如上图所示,所有的请求都会进行排队,等待专门计算的单线程处理,当发现需要I/O操作后,就把请求抛到专门处理I/O操作的异步线程去处理,当I/O处理结束后,再将callback function返回到计算的排队序列中。

 

总结一下两个的优缺点:

 


优点

缺点

适用场景

传统Web服务器

CPU利用充分

如果有大量I/O,需要维持巨大的线程池;

上下文切换浪费时间

I/O操作少的情况下

Node.js

I/O操作多,性能优异

不能充分利用多核CPU服务器

均适用

 

再提一嘴,提到Node.js,很多时候会提到Express.js,名字很像哦!但是他俩的关系很像JVM和Spring的关系。Node.js是一个JS运行的环境,试想一下要直接在JVM上写Java原生代码很费劲,利用Express.js这个轻量级框架,就轻松多了。

 

本文节选自BitTigerCS503 全栈开发实战精品课,5月18日正式开课!报名最后五天截止!点击阅读原文马上预约咨询!


以上是关于单线程架构的Node.js如何实现异步操作?的主要内容,如果未能解决你的问题,请参考以下文章

浅谈Node.js单线程模型

浅谈Node.js单线程模型

单线程高并发的运行时环境

如何在一个类中实现异步

node.js的单线程异步是什么意思呢?(转)

异步编程如何在单线程编程模型中工作?