Node.js 优缺点
Node.js 是单进程。
好处就是:
- 简单
- 高性能,避免了频繁的线程切换开销
- 占用资源小,因为是单线程,在大负荷情况下,对内存占用仍然很低
- 线程安全,没有加锁、解锁、死锁这些问题
反之,看 PHP
对比,Node.js 的处理:
坏处就是:
如何解决高并发?
node 使用异步 IO 和事件驱动(回调函数)来解决这个问题。
一般来说,高并发解决方案会提供多线程模型,为每个业务逻辑提供一个线程,通过系统线程切换来来弥补同步 I/O 调用的时间开销。像 apache,是一个请求一个线程。
而 node.js 使用的是单线程模型,对所有 I/O 都采用异步的请求方式,避免频繁的上下文切换,在 node.js 执行的时候维护着一个事件队列;程序在执行时进入事件循环等待下一个事件到来,每个异步 I/O 请求完成后都会被推送到事件队列中的等待执行。
比如说:
对于一个简单的数据库访问操作,传统方式是这样实现的:
res = db.query('SELECT * from some_table');
res.output();
代码执行到第一行的时候线程会阻塞,等待 query 返回结果,然后继续处理。由于数据库查询、磁盘读写、网络通信等原因(所谓的 I/O)阻塞时间会非常大(相对于 CPU 始终频率)。对于高并发的访问,一方面线程长期阻塞等待,另一方面为了应付新情求而不断添加新线程,会浪费大量系统资源,同时线程的增加也会也会占用大量的 CPU 时间来处理内存上下文切换。看看 node.js 怎么处理:
db.query('SELECT * from some_table', function(res) {
res.output();
});
query 的第二个参数是一个回调函数,进程执行到 db.query 的时候不会等待结果返回,而是直接继续执行下面的语句,直到进入事件循环。当数据库执行结果返回的时候会将事件发送到事件队列,等到线程进入事件循环后才会调用之前的回调函数。
node.js 的异步机制是基于事件的,所有的 I/O、网络通信、数据库查询都以非阻塞的方式执行,返回结果由事件循环来处理。node.js 在同一时刻只会处理一个事件,完成后立即进入事件循环检查后面事件。这样 CPU 和内存在同一时间集中处理一件事,同时尽量让耗时的 I/O 等操作并行执行。
事件循环机制
所谓事件循环是指 node.js 会把所有的异步操作使用事件机制解决,有个线程在不断地循环检测事件队列。
node.js 中所有的逻辑都是事件的回调函数,所以 node.js 始终在事件循环中,程序入口就是事件循环第一个事件的回调函数。事件的回调函数中可能会发出 I/O 请求或直接发射( emit)事件,执行完毕后返回事件循环。事件循环会检查事件队列中有没有未处理的事件,直到程序结束。node.js 的事件循环对开发者不可见,由 libev 库实现,libev 不断检查是否有活动的、可供检测的事件监听器,直到检查不到时才退出事件循环,程序结束。
如图所示:
libuv 是一个高性能事件驱动的程序库,封装了 Windows 和 Unix 平台一些底层特性,为开发者提供了统一的 API.
因此,node.js 是单线程,异步非阻塞。
但毕竟,如何弥补单线程缺陷?是不是有异步非阻塞,就可以高枕无忧了?
不是的。
1)CPU 密集型任务存在短板
如上所述,nodejs 的机制是单线程,这个线程里面,有一个事件循环机制,处理所有的请求。如图所示。在事件处理过程中,它会智能地将一些涉及到 IO、网络通信等耗时比较长的操作,交由 worker threads 去执行,执行完了再回调,这就是所谓的异步 IO 非阻塞吧。但是,那些非 IO 操作,只用 CPU 计算的操作,它就自己扛了,比如算什么斐波那契数列之类。它是单线程,这些自己扛的任务要一个接着一个地完成,前面那个没完成,后面的只能干等。因此,对 CPU 要求比较高的 CPU 密集型任务多的话,就有可能会造成号称高性能,适合高并发的 node.js 服务器反应缓慢。
2)无法利用 CPU 的多核
最开始,线程只是用于分配单个处理器处理时间的一种机制。但假如操作系统本身支持多个 CPU / 内核,那么每个线程都可以得到一个不同自己的 CPU / 内核,实现真正的 “并行运算”。在这种情况下,多线程程序可以提高资源使用效率。Node.js 是单线程程序,它只有一个 event loop,也只占用一个 CPU / 内核。现在大部分服务器都是多 CPU 或多核的,当 Node.js 程序的 event loop 被 CPU 密集型的任务占用,导致有其它任务被阻塞时,却还有 CPU / 内核处于闲置的状态,造成资源的浪费。
解决方案
利用原生模块或第三方模块,开辟进程或子进程,用于处理这些特殊的任务。
3)如果有异常抛出,因为是单线程,整个项目将不可用。
但这归根到底是代码的问题,糟糕的代码,不管什么体系,都会有问题,即使不崩溃。解决办法是用 pm2 等工具来运行?
Nodejs 与 JavaScript 的关系
nodejs 本身不是开发语言,它是一个工具或者平台,在服务器端解释、运行 javascript;coffeescript 属于 nodejs 体系,算是一种新的开发语言,但它的目的在于最后编译成 javascript。
nodejs 利用 Google V8 来解释运行 javascript,但是系统真正执行的代码是用 C++ 写的。javascript 做的只是调用这些 API 而已。因此,并无执行效率的问题。
nodejs 适用场景
1、RESTful API
这是适合 Node 的理想情况,因为您可以构建它来处理数万条连接。它仍然不需要大量逻辑;它本质上只是从某个数据库中查找一些值并将它们组成一个响应。由于响应是少量文本,入站请求也是少量的文本,因此流量不高,一台机器甚至也可以处理最繁忙的公司的 API 需求。
2、实时程序
比如聊天服务
聊天应用程序是最能体现 Node.js 优点的例子:轻量级、高流量并且能良好的应对跨平台设备上运行密集型数据(虽然计算能力低)。同时,聊天也是一个非常值得学习的用例,因为它很简单,并且涵盖了目前为止一个典型的 Node.js 会用到的大部分解决方案。
3、单页 APP
ajax 很多。现在单页的机制似乎很流行,比如 phonegap 做出来的 APP,一个页面包打天下的例子比比皆是。
。。。
总而言之,NodeJS 适合运用在高并发、I/O 密集、少量业务逻辑的场景