Node.js应用实战和工作原理解析

533 查看

Node.js是一个基于Chrome JavaScript运行时建立的开发平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动,非阻塞I/O模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用,例如移动应用里的消息模块。


为满足云智慧透视宝用户对Node.js的代码级性能监控需求,我们的程序猿Else对Node.js的工作原理和运行机制进行了大量的深入研究,而本次分享正是来自Else的心得体会。
本文分为俩个部分:
第一部分:通过应用层面,给大家演示什么Node.js,它能做什么,怎么去做。
第二部分:在大家对Node.js有个基本认识之后,从理论层面谈谈Node.js的运行机制。

Node.js应用解析

Node.js是能够跨平台的,今晚我们分享将基于windows,但linux或者macro大致相同,如果有疑问大家可以相互探讨,本人邮件地址 else.li@yunzhihui.com qq:287798478
nodejs环境搭建
首先登陆node官网下载安装包,地址是:nodejs.org/en/


下载完成之后,安装步骤就是傻瓜式的下一步下一步:


安装完成之后在windows下打开powershell,然后执行 " node -v "就能查看到我们node的版本信息,这也就说明我们的node已经安装成功。


第一个demo
之后我们找个目录新建一个项目文件夹,然后在项目文件夹下面新建一个hello__.js的文件,将如下代码写入hello__.js的文件:


而后我们cd到项目目录下,运行node hello__.js


然后在浏览器里输入127.0.0.1:8899就能直接访问我们的服务了


我们在代码中用了 var http = require("http"); 这种方式引用了node的一个自身模块http,模块可以理解成DotNet的类库、Java的包,然后应用这个http模块快速建了一个服务,这也是node给开发人员带来的便利。
NPM
说到便利,对于node.js还必须要提到NPM,在上个demo中我们用到var http = require("http"); 来引用模块,我们也说到了http是node自身的模块,那么如果需要引用一些node的非自身模块,应该如何做呢?
这个任务的第一步就交给NPM,在安装node的同时已经将NPM工具安装完成,我们只需要执行NPM -v就能查看NPM的版本,直接输入NPM可以查看相关帮助。
NPM的最常见用法就是安装依赖模块,当安装完成之后我们的项目就能对安装的依赖模块进行引用。现在我们拿 express模块来举个栗子,首先需要cd 到项目的node_modules目录下,然后运行npm install express:


当你看到这样的界面的时候就说明模块已经安装成功,回到项目目录下就能看见模块目录下新增了一个express目录,然后在项目目录下创建一个hello.js的脚本文件,文件内容如下:


下一步就是启动这个项目,直接运行 node .hello.js


然后在浏览器里输入地址127.0.0.1:8081


这种方式引用模块非常方便。那么我们是不是也可以编写自己的第三方模块上传到npm库给他人使用呢?答案是肯定的!接下再举个栗子给大家说明下,首先在项目根目录下创建一个npmtest文件夹:


在当前目录下创建一个hello.js文件,内容如下:


然后运行npm init 命令,在运行命令之后会要求你输入一些模块打包的描述信息,这些信息最终会生成一个包的描述文件叫package.json


输入所有信息之后会问你"Is this ok?",输入yes就完成了打包过程。查看文件夹下面会增加一个描述包文件:


然后大家再look下我们生成描述文件的内容:


可以看到这些信息都是刚才输入的内容。包打好了,接下来就是上传到仓库,上传仓库前我们先注册一个账号npm adduser,然后输入用户名、密码还有邮箱地址就可以了。
现在万事具备了,东风来吧,运行npm publish ,就可以把我们包上传了:

注意publish后面有一个点 ”.“。当我们的包上传完成之后,通过调用来做个验证,换个目录下来执行 npm install cloudwise_else_npmtest:


在我们的目录里查看,就知道创建的模块已经引用过来了:


上面我们介绍Node.js的环境搭建,模块安装、引用,以及如何创建和引用自己的模块,并对每一个点都做了相应的示例。
调试工具
现在我们已经能够让项目run起来了,项目运行过程中如果出现异常怎么办,这是程序猿尤为关心的。在我接触的PHP开发者中,他们比较习惯用日志去跟踪,本人是DotNet的忠实粉丝,江湖流传宇宙最强IDE的Visual Studio给DotNet开发者提供了非常强大的调试功能,可以让我们随心所欲的调试,快速准确的定位到Bug。所以日志调试让我很不能接受,如此火热的Node.js肯定也给开发者提供了不错的debug工具。
首先还是用npm来安装,命令如下:
npm install -g node-inspector
启动的时候我们会在中间加一个debug,命令如下:
node debug .hello.js
接下来我们启动调试插件 node-inspector.cmd,此处linux跟windows有点差别,linux下没有后面cmd,然后打开浏览器(当前此插件只支持chrome跟Opera),地址如下:http://127.0.0.1:8080/?port=5858


当我们在代码中打断点的时候,程序就会捕获到断点,然后在这里添加监视,也可以单步执行或者逐过程逐语句,爱怎么玩就怎么玩。说到这里我们对Node.js是什么、能做什么、怎么做有了基本的认识,那么是不是就可以开始coding工作了呢?
完全可以的!有人或许会问,Node.js能做的事情php、java、.net也都能做,为什么要选择Node.js呢,难道单单是因为他不用去搭建Apache、IIS、Tomcat吗?接下来我就根据自己的理解和大家探讨下Node.js能够在最近几年被聚光的缘由。

Node.js运行机制解析

当我们搜索Node.js时,夺眶而出的关键字就是 "单线程,异步I/O,事件驱动",应用程序的请求过程可以分为俩个部分:CPU运算和I/O读写,CPU计算速度通常远高于磁盘读写速度,这就导致CPU运算已经完成,但是不得不等待磁盘I/O任务完成之后再继续接下来的业务。
所以I/O才是应用程序的瓶颈所在,在I/O密集型业务中,假设请求需要100ms来完成,其中99ms化在I/O上。如果需要优化应用程序,让他能同时处理更多的请求,我们会采用多线程,同时开启100个、1000个线程来提高我们请求处理,当然这也是一种可观的方案。
但是由于一个CPU核心在一个时刻只能做一件事情,操作系统只能通过将CPU切分为时间片的方法,让线程可以较为均匀的使用CPU资源。但操作系统在内核切换线程的同时也要切换线程的上线文,当线程数量过多时,时间将会被消耗在上下文切换中。所以在大并发时,多线程结构还是无法做到强大的伸缩性。
那么是否可以另辟蹊径呢?!我们先来看看单线程,《深入浅出Node》一书提到 "单线程的最大好处,是不用像多线程编程那样处处在意状态的同步问题,这里没有死锁的存在,也没有线程上下文切换所带来的性能上的开销",那么一个线程一次只能处理一个请求岂不是无稽之谈,先让我们看张图:

Node.js的单线程并不是真正的单线程,只是开启了单个线程进行业务处理(cpu的运算),同时开启了其他线程专门处理I/O。当一个指令到达主线程,主线程发现有I/O之后,直接把这个事件传给I/O线程,不会等待I/O结束后,再去处理下面的业务,而是拿到一个状态后立即往下走,这就是“单线程”、“异步I/O”。


I/O操作完之后呢?Node.js的I/O 处理完之后会有一个回调事件,这个事件会放在一个事件处理队列里头,在进程启动时node会创建一个类似于While(true)的循环,它的每一次轮询都会去查看是否有事件需要处理,是否有事件关联的回调函数需要处理,如果有就处理,然后加入下一个轮询,如果没有就退出进程,这就是所谓的“事件驱动”。