NodeJS学习笔记(一)——异步I/O的理解

写在开篇的题外话一个偶然的机会让我接触到了前端开发的相关知识,从此以后就陷入了前端开发的怀抱,无法自拔。在做完一个又一个项目之后,才知道前后端的“恋爱”有多么痛苦,欲分不分的藕断丝连让其他开发者头疼不已。在学习了NodeJS的一些知识后,才发现这简直就是解救前端开发人员的利器啊!遂开始学习,在此记录下学习心得,如有理解的不正确的地方,还希望批评指正。

Node是什么

在刚接触并提起Node.js时被人问到最多的一个问题就是:这到底是个什么东西?最初我理解Node是一个前端的服务器,类似我们常用的tomcat、apache等,然而这个理解是错的。Node只是一个Javascript运行平台而已。

最初的Javascript只是基于网页的小脚本,要依赖web应用环境才可以运行。然而Node的出现让我们可以像Java一样,编写系统级、服务端的代码,并在Node平台上直接运行。然而单纯使用Node作为服务器的底层又不是很现实,使用它作为前端的服务器再合适不过了,这样前端和后端可以更好的进行分离,底层接口提供合适的数据结构,交给前端,前端可以自己构建高效率、可用性高的接口,只要接口之间的数据结构不发生变化,前后台之间是不会造成任何影响的。

从异步I/O说起

Node的高性能是离不开异步I/O的,那么我们就从异步I/O开始理解一下Node的工作机制。

第一段Node代码从Hello World开始

NodeJS学习笔记(一)——异步I/O的理解_第1张图片

Node的模块机制——弥补javascript先天对模块功能的欠缺

在以上的例子中,我们创建了一个服务,监听了8686端口。在监听到8686端口请求之后,调用了函数fInitServer,fInitServer方法是针对用户请求连接事件的函数。这个服务中传入的参数为webrequestresponse在回调函数中,执行了请求连接后服务器的响应,即通过response浏览器输出Hello World字符串

var http = require(‘http’);

这句代码引入了Node核心的http模块Node引入了一部分CommonJS规范,使用require引入模块。Node引入模块的流程为:

分析路径->查找文件->编译执行

Node的模块分为两类:核心模块和文件模块。

核心模块是Node提供的模块,核心模块已经被编译过,它在Node进程启动时就被加载到内存中,所以在引用核心模块时,不需要进行路径的分析和源文件的查找,并优先判断,使用核心模块的效率是很高的。

文件模块是用户自定义开发的模块,在引入用户自定义模块时,首先要根据require传入的路径(可以是相对路径,也可以是绝对路径)进行解析,定位到模块源文件,并对源文件进行扩展名分析等操作后,对模块进行编译,再将模块放到缓存中,方便下次使用。由于Node查找自定义模块路径是逐级向上递归查找的,所以文件路径越深,查找耗时会越多,所以在引入自定义模块时路径要尽量精确。

提示:在使用node的express框架,运行js可能会遇到这个问题:cannot find module “express”。由Node加载模块的原理可以看出,出现这个错误的原因是没有在工程路径下安装express模块,Node执行时会递归查找node_modules目录,如果找不到则会报无法找到模块的问题。

异步I/O的实现

Node是如何在用户请求后,调用fInitServer函数的呢?

操作系统对I/O的操作分为阻塞I/O和非阻塞I/O阻塞I/O造成了CPU的等待,使CPU不能得到充分的利用;而非阻塞I/O虽然不必等待完整I/O的返回,但需要通过轮询重复的调用判断操作,这种判断操作也是对CPU的一种浪费。我们希望非阻塞I/O可以免去轮询的步骤,在实际I/O操作完成后,通过返回完成信号通知应用程序即可。

Node使用了观察者模式和事件循环机制来实现这种异步I/O这里以去餐厅消费为例,异步调用相当于去餐厅就餐的顾客,顾客将菜单交给服务员,即NodeI/O观察者,厨房即Node的事件循环机制询问服务员是否还有要做的菜,服务员将顾客的菜单交给厨房处理。

NodeJS学习笔记(一)——异步I/O的理解_第2张图片

Node有自身的事件循环机制,从I/O观察中取得是否还有待执行的事件,如果有事件,调用对应的I/O线程执行,这时会生产一个请求对象,这个请求对象记录了提交I/O的状态、记录了事件的回调等信息,在I/O线程执行完毕后,会修改请求对象的执行状态,并通知事件循环系统,将处理完成的I/O信息通知I/O观察者,执行回调函数。

从上述流程可以看出,Node是以事件为驱动来实现异步I/O

你可能感兴趣的:(Node)