1.5 Node的应用场景
在进行技术选型之前,需要了解一项新技术具体适合什么样的场景,毕竟合适的技术用在合适的场景可以起到意想不到的效果。关于Node,探讨得较多的主要有I/O密集型和CPU密集型。
1.5.1 I/O密集型
在Node的推广过程中,无数次有人问起Node的应用场景是什么。如果将所有的脚本语言拿到一处来评判,那么从单线程的角度来说,Node处理I/O的能力是值得竖起拇指称赞的。通常,说Node擅长I/O密集型的应用场景基本上是没人反对的。Node面向网络且擅长并行I/O,能够有效地组织起更多的硬件资源,从而提供更多好的服务。
I/O密集的优势主要在于Node利用事件循环的处理能力,而不是启动每一个线程为每一个请求服务,资源占用极少。
1.5.2 是否不擅长CPU密集型业务
换一个角度,在CPU密集的应用场景中,Node是否能胜任呢?实际上,V8的执行效率是十分高的。单以执行效率来做评判,V8的执行效率是毋庸置疑的。
我们将相同的斐波那契数列计算(F0=0, F1=1, Fn=F(n-1)+F(n-2)(n≥2))分别用各种脚本语言写了算法实现,并进行了n = 40的计算,以比较性能。这个测试主要偏重CPU栈操作,表1-1是其中一次运算耗时的排行。在这些脚本语言中(其中C和Go语言是静态语言,用于参考), Node是足够高效的,它优秀的运算能力主要来自V8的深度性能优化。
表1-1 计算斐波那契数列的耗时排行
这样的测试结果尽管不能完全反映出各个语言的性能优劣,但已经可以表明Node在性能上不俗的表现。从另一个角度来说,这可以表明CPU密集型应用其实并不可怕。CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起。但是适当调整和分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起,这样既可同时享受到并行异步I/O的好处,又能充分利用CPU。
关于CPU密集型应用,Node的异步I/O已经解决了在单线程上CPU与I/O之间阻塞无法重叠利用的问题,I/O阻塞造成的性能浪费远比CPU的影响小。对于长时间运行的计算,如果它的耗时超过普通阻塞I/O的耗时,那么应用场景就需要重新评估,因为这类计算比阻塞I/O还影响效率,甚至说就是一个纯计算的场景,根本没有I/O。此类应用场景或许应当采用多线程的方式进行计算。Node虽然没有提供多线程用于计算支持,但是还是有以下两个方式来充分利用CPU。
❑ Node可以通过编写C/C++扩展的方式更高效地利用CPU,将一些V8不能做到性能极致的地方通过C/C++来实现。由上面的测试结果可以看到,通过C/C++扩展的方式实现斐波那契数列计算,速度比Java还快。
❑ 如果单线程的Node不能满足需求,甚至用了C/C++扩展后还觉得不够,那么通过子进程的方式,将一部分Node进程当做常驻服务进程用于计算,然后利用进程间的消息来传递结果,将计算与I/O分离,这样还能充分利用多CPU。
CPU密集不可怕,如何合理调度是诀窍。
1.5.3 与遗留系统和平共处
有人会说:“JavaScript一统前后端了,将来会不会干掉其他的语言?”言语中充满了危机感。
在Web端,过去大多都是同步的方式编写的程序,这种串行调用下层应用数据的过程中充斥着串行的等待时间,如果采用多线程来解决这种串行等待,又或多或少地显得小题大作。在Node中,语言层面即可天然并行的特性在这种场景中显得十分有效。对于已有的稳定系统,并非意味着我们要抛弃掉。
LinkedIn在他们的移动版网站上的实践非常典型地说明了这个问题。旧有的系统具有非常稳定的数据输出,持续为传统网站服务,同时为移动版提供数据源,Node将该数据源当做数据接口,发挥异步并行的优势,而不用关心它背后是用什么语言实现的。
这方面,国内的雪球财经也有很好的实践。雪球财经是从旧有的Java项目中分离出一个子项目,在这个子项目中,没有继续采用Java/JSP而是采用Node来完成Web端的开发,使得前端工程师在HTTP协议栈的两端能够高效灵活地开发,避免了Java烦琐的表达;另一方面,又利用Java作为后端接口和中间件,使其具有良好的稳定性。两者互相结合,取长补短。
1.5.4 分布式应用
阿里巴巴的数据平台对Node的分布式应用算是一个典型的例子。分布式应用意味着对可伸缩性的要求非常高。数据平台通常要在一个数据库集群中去寻找需要的数据。阿里巴巴开发了中间层应用NodeFox、ITier,将数据库集群做了划分和映射,查询调用依旧是针对单张表进行SQL查询,中间层分解查询SQL,并行地去多台数据库中获取数据并合并。NodeFox能实现对多台MySQL数据库的查询,如同查询一台MySQL一样,而ITier更强大,查询多个数据库如同查询单个数据库一样,这里的多个数据库是指不同的数据库,如MySQL或其他的数据库。
这个案例其实也是高效利用并行I/O的例子。Node高效利用并行I/O的过程,也是高效使用数据库的过程。对于Node,这个行为只是一次普通的I/O。对于数据库而言,却是一次复杂的计算,所以也是进而充分压榨硬件资源的过程。