Node.js的最大特点就是采用异步式I/O与事件驱动的架构设计。传统的架构通过多线程模型来解决高并发,也就是为每个业务逻辑提供一个系统线程,通过系统线程的切换来弥补同步I/O调用时的时间开销。Node.js使用的是单线程模型,对所有I/O都是用异步请求的方式,避免了频繁的上下文切换。Node.js在执行过程中会维护一个事件队列,程序执行时进入事件循环等待下一个事件到来,每个异步I/O请求完成后会被推送到事件队列,等待程序进行处理。
例如对数据库的简单查询操作:
传统实现方式:
res = db.query('select * from table');
res.output();
Node.js实现方式:
db.query('select * from table',function(res){
res.output();
});
传统的实现方法的代码执行到第一行的时候,线程就会阻塞,等待数据库返回结果,然后再继续处理。由于数据库的操作可能涉及磁盘读写和网络通信,其延时可能向当长。对于高并发的访问,一方面线程长时间 阻塞等待返回结果,另一方面为了应付新需求不断增加线程,因此浪费大量系统资源,同时线程也会占用大量的CPU来处理上下文切换,而且还容易遭受低速链接的攻击。
Node.js处理方法中第二个参数为一个回调函数,进程执行到db.query时,不会等待数据库返回结果,而是直接执行后面的语句,直到进入事件循环。当数据库返回查询结果时,会将事件发送到事件队列,等到线程进入事件循环后,才会调用函数执行后面的逻辑。
Node.js的异步机制是基于事件的,所有的磁盘I/O、网络通信、数据查询都以非阻塞的方式请求,返回的结果由时间循环来处理。Node.js进程在同一时间只会处理一个事件,完成后立即进入事件循环检查并处理后面的事件。这样做的好处是,CPU和内存同一时间集中处理一件事情,同时让耗时的I/O操作并行执行。对于低速直接攻击,Node.js只是在事件队列中增加请求,等待回应,因而不会哟偶任何多线程开销,防止恶意攻击。
这种异步事件模式的弊端是不符合开发者的常规线性思路,往往需要把一个完整的逻辑分为一个个事件,怎加了开发和调试难度。