前言
Node.js 是运行在服务端的 JavaScript,它具备可以处理高并发的能力,这一章作为nodejs系列文章的第一章,我们将介绍nodejs之所以性能好的原因
正文
共享状态的并发
node为javascript引入了一个复杂的概念,共享状态的并发,通俗讲,node中你需要对回调函数如何修改当前内存中的变量特别小心,除此之外,你还要特别注意对错误的处理是否会潜在改变这些状态导致整个进程不可使用,什么意思呢,我们举个例子进一步说明:
这里我们假设一个用户像node服务器同时发出/books的请求,结果会是怎么样的呢,node会将完整的图书列表返回给第一个请求,但是把一个空的图书列表返回给第二个请求。为什么会有这样一个与传统服务器端语言不同的结果呢
两者区别主要就在于基础架构上,node采用的是一个长期运行的进程,相反,Apache会产出多个线程(每个请求一个线程),每次都会刷新状态。而在传统语言中,变量books会被重新赋值,而node中不然,serveBooks函数会被再次调用,且作用域中的变量不受影响,不会重新赋值(也就是仍然保持被修改为空数组后的状态)
阻塞
node.js和javascript一样,使用了事件轮询,因为毕竟是基于javascript的API,这也意味着node.js虽然是单线程执行的,但是却不会被阻塞。我们来看下面的例子:
下面这段代码在我电脑上的运行结果是这样的:
为什么和预期的结果不一样呢,因为事件轮询被javascript代码阻塞了,由于第一个定时函数的回调函数执行了很长时间(循环次数很多),所以下一个事件轮询执行的时间就远远超过了2s。所以这样的行为方式并不是很理想的,事件轮询是Node IO的基础核心,意味着如果出现阻塞,HTTP服务器每秒处理的请求数量就会减少,效率也会降低
非阻塞
正因为如此,许多优秀的Node模块都是非阻塞的,执行任务也都采用了异步的方式。既然执行时只有一个线程,也就是说,当一个函数执行时,同一时间不可能有第二个函数也在执行,那么node.js又是如何做到高并发的呢
其实Node的最大并发量就是1,因为是单线程,并不提供真正的并行操作,关键在于,在调用堆栈执行非常快的情况下,同一时刻你无须处理多个请求,v8搭配非阻塞io,就可以帮助从执行速度和非阻塞上实现伪并行
错误处理
node应用依托在一个拥有大量共享状态的大进程中,在一个HTTP请求,如果某个回调函数发生错误,整个进程都会出错:
因为错误未被捕获,所以访问的时候会导致进程崩溃
node这么处理的原因就是因为,在发生未被捕获的错误时,进程的状态就不确定了,之后就可能无法正常工作了,并且如果错误始终不被处理的话,就会一直抛出意料之外的错误,这样很难调试。所以,错误处理中,每一步都很重要,因为它能让你书写更安全的程序,并且不丢失出发错误的上下文信息
小结
我们知道Node通过单线程的执行环境,提供了极大的简便,不过,也正因如此,当你书写网络应用时,要尽可能避免使用同步IO,并且该线程中的所有状态都是维护在一个内存空间中的,换句话说,写程序的时候修改变量的时候就要格外小心