我与Node.js重新认识的第一周 - Node.js 风格特点

1,418 阅读5分钟
书接上回,

慢慢悠悠读了《深入浅出node.js》(后面简写作《深浅》)以及《node.js高级编程》(后面简写作《高级》)的开头部分,查了不少网络资料,关于node.js的特点有了一定了解。深究起来需要再看看操作系统以及网络的知识。

《深浅》中提到了四个node.js的特点:异步I/O,事件与回调函数,单线程以及跨平台。
《高级》中基本是一笔带过,提到了纯事件驱动以及非阻塞。

其中对于异步I/O以及单线程这两个特性,我认为可以分以下三个方向来概述为什么node.js把天赋点在了这它们上面?

1. 用户体验

从前端加载的角度来看,比较直接。先说为什么JavaScript用异步?!我们反着思考,如果JavaScript是同步的,会有什么问题。请求一个用户管理页面,用js加载若干资源,先加载一个用户头像,再加载第二个,第三个。。。GG,用户以为卡死了,二话不说给你关了(《深浅》中提到脚本时间超过100毫秒,用户就会有卡顿的感觉;而且运行在单线程上的JavaScript还与UI渲染公用一个进程)。实际中,JavaScript的异步消除/减弱了UI阻塞的现象,同步时候加载资源的总时间是X+Y+Z,异步下的总时间则是max(X+Y+Z),可见差距。另外《深浅》中来提到目前网络发展,分布式应用普及,前面XYZ的数值在增长,那么可以想象到X+Y+Z和max(X+Y+Z)的差距肯定越来越大。由此可以看出异步大法好,大家都说屌!那么node.js选择异步I/O也就顺理成章,从后端做到异步,提升资源响应速度,那么随之前端的用户体验也就会更好

                                    

2. 系统资源

一组任务需要被执行,可以通过两种方式:单线程串行执行,多线程并行执行。

单线程串行执行,问题是:同步阻塞! I/O需要等待X步结束,才能进行到X+1步 - 原因是早期分时系统:cpu轮流给不同用户服务(服务的时间单位是时间片),给A服务完了第x步,可能就去给B服务第y步,之后才又回到属于给A服务的时间片,然后再给A服务x+1步(这也能看出来为啥I/O需要等待,上一步结束才能执行下一步)。为了继续执行A的操作,从x到x+1步骤的上下文就需要系统维护和交换,那么当进程很多,就会造成性能的下降。慢的任务就会拖慢整个处理进度- 这样难道一无是处么,并不是,好处是顺序编程,逻辑比较容易,易于表达 。

多线程并行执行,问题是:编程时要考虑锁,状态同步的问题,稍有不慎,家毁人亡! 

因此,node.js的解决方案是:利用单线程,远离多线程死锁、状态同步等问题;利用异步I/O,让单线程远离阻塞,以更好地使用CPU。(《深浅》原句)

                                 

3. 应用场景

node.js特点与其应用场景我感觉互为问题与答案,考虑到web app是当今最常见的I/O密集型任务,node.js选择了异步I/O,单线程以及事件驱动,来增强性能。同时,也正是因为node.js
的这些特点,使得它更加适合I/O密集型的应用场景。(关于node.js是否适合计算密集型任务,《深浅》中做了解释与对比) 

---------------------------------------------------------------------

下面是另外几点令我印象深刻的地方:

  • 《深浅》还提到:为了提升性能增加进程数量,这种方法是无法提升资源利用率。他用到了『加三倍服务器』的例子,下面是我从豆瓣上找到的一个解释:(加三倍服务器是否能提升性能)要看系统本身的架构,不一定增加三倍服务器就能档得住三倍的用户一起来点的。因为服务器增多之后,其间通信的成本也增加了,而且如果存在中心节点,那么那几个节点还是会变成瓶颈。跟高速公路堵车对比,加服务器并不是“四车道变八车道”那么简单,很可能多修几条路以后1)十字路口也变多了2)支路多了以后主干道堵得更厉害。” 
  • 如果深究异步I/O这个东西,《深浅》第三章做了更加细致的讲解,涉及到了异步的几种实现方式,以及Node是怎么实现异步I/O的。原来之前说的异步,是理想的非阻塞异步I/O: 


                                          (出自《深入浅出node.js》)

       真正到了实现的时候,Node其实是用了多线程的方式模拟出来这一理想的效果。 


                                          (出自《深入浅出node.js》)

     等等,多线程?Node不是JavaScript,是单线程么,上面特点不说了是单线程么?是不是说漏嘴了。那必须不是,《深浅》书中提到,Node的单线程指的JavaScript运行在单线程中,而实现Node的异步I/O功能的则是线程池/多线程。 
  • node.js既然有异步I/O的特点,是不是就可以肆意妄为了,只要异步就一定非阻塞呢?答案肯定是不啊(考这么多年试:像这种极端的问题,答案肯定是否定的)。如果我们在主线程做过多的任务,或者很多计算密集型任务,那么可能会导致主线程的卡死,影响整个程序的性能,这不就阻塞你异步的脚步了。


如果有什么地方总结的不对,希望大家一起交流!