Node.js高性能挖掘— 1异步编程
发布于 8 年前 作者 classfellow 4235 次浏览 来自 分享

本文系原创,转载请注明出处~ Node高性能——异步编程

Node.js的起点高,一诞生就是冲着高性能高并发去的。过去,使用node写复杂的业务逻辑,callback的堆砌,便会陷入到‘回调金字塔’。而目前es6的Generator使得node变得适合做业务逻辑,事实上,node能够胜任任何复杂的业务逻辑。node高性能的来源,得益于其完全异步的编码范式。 witch61截图20160605131646082.jpg 为了理解异步编程的高效性,可以举一个简单例子加以说明,按照目前帝都的政策,外地牌照的车上路需要办理进京证,而办理进京证是一个很花时间的事儿。如果每个人都自己去办理,那么大家都过去,因为办理窗口有限,大家都的排队。而找个第三方,交点钱,给第三方办,则排队的人变少,不仅节省时间,还能够省事儿。 witch61截图20160605131913540.jpg 假如来了一个任务,交给第三方去办,则第三方就有一个接单队列,这位第三方只需要拿着所有的接单,去办理点儿挨个办理即可。你花了代理费,却节约了时间和精力。

假如一个请求到来,创建一个进程或者线程处理这个任务,就好比亲自去办证,这不仅浪费自己的时间,也会造成资源浪费,交通拥堵。而交给第三方代理的处理方式,就是任务来了,不必创建新的进程和线程,而是交给一个已经存在的第三方,其生成一个队列,按序处理。在系统I/O吞吐一定的情况下,不损性能,且节约大量资源。

想象一下,如果来了一千个并发请求,按照创建线程或进程的方式,你能想象一个服务器运行这么多进程或线程,CPU花费在上下文切换是多么低效。CPU和内存资源的内耗挤压了真正花费在处理业务逻辑的时间,造成服务器性能严重下降。

异步编程的概念说起来简单,但真正做好,却不那么容易。事实上,这涉及到很多高阶的编码技术。本文以读取文件的I/O操作举例,为了便于说明,假设有一个异步读取文件函数 ReadFile 。试想,主线程请求读取文件,其发起了一个异步读取操作,ReadFile 之后立马返回,进入到下一次循环,接受其他任务,这正是node的运行模式。操作系统读完文件后,通知主线程,主线程在下一次循环中取到数据。这个过程需要解决的一个问题是,当主线程取到数据后,如何识别数据是谁发起的? 以及得到数据之后怎么处理呢?

可见,做一个数据型的任务队列难以闭合异步编程。事实上,这里需要的是一个类似于闭包的东西,因为闭包包含了可执行代码和代码需要的上下文,此处,主线程调用闭包的函数,沿着发起读取文件时候的执行环境继续运行后续逻辑。

你会发现,采用这种编程模式,假如主线程同时发起一千个I/O操作,你不需要创建一千个线程去读,然后跟主线程交互。采用异步编程和闭包保存上下文,操作系统把数据准备好之后,把闭包重新传给主线程的MessageLoop,主线程在下一次循环中,执行数据准备好之后的下一步操作。

我们听过一句话,Node.js能够最大限度压榨硬件资源,其根源在这里。换句话说,CPU把绝大多数时间花在处理实际业务逻辑上,而不是线程进程的上下文切换和无效等待上。你的服务器运行一个高并发node服务,还可以作为一个cpu密集型任务服务器,都不耽误。

总结: 理解node高性能编程和异步编程模式的关键点在于, 1 node处理任务不会创建一堆线程 2 node调用异步函数,具体I/O操作由操作系统实现,操作系统读取成功之后,通知调用线程,主线程在下一次循环中,执行闭包定义的函数。

以上讨论,涉及到系统底层的编码,要完整演示上述过程,最好的方法是用实际的代码跑一遍,演示任务队列,C++闭包等C++编码模式和技术。未来在这个系列里,将用Windows 的IOCP的方式,演示异步编程以及采用C++闭包的方式执行回调。

下一篇 Node.js构建高性能服务器之二—Generator

3 回复

讲的很通俗 nice

lz说的C++ 闭包等 C++ 编码模式是什么意思呢, 还有 IOCP, 这些不是很明白 期待续集

你的网站做的很清新^^

通俗易懂+1

回到顶部