nodejs并不是一门新的语言,与Java,php开发语言的平台也是不相同的,虽然他是JavaScript但是它并不是JavaScript的框架。nodejs是让JavaScript运行在服务器端的开发平台。当一种事务在一个领域独孤求败的时候就会想着进入另外一个领域,JavaScript已经在近几年已经一统的前端的江湖,所以已经开始进入服务端的领域。Node.js是基于Chrome JavaScript运行时建立的一个平台,使用C++编写的,实际上它是对Google Chrome V8引擎进行了封装,它主要用于创建快速的、可扩展的网络应用。Node.js采用事件驱动
和非阻塞I/O模型
,使其变得轻微和高效,非常适合构建运行在分布式设备的数据密集型实时应用。
以前是没有人会想到用JavaScript作为自己的web server 这是因为什么呢?因为JavaScript是比较慢和乱
- V8引擎解决了JavaScript慢的问题
- commonjs解决了乱的问题
- Node的特性是事件驱动
- 为了弥补JavaScript在服务器端的空白
当计算机调度线程进行I/O操作命令后,由于文件的读写或者网络通信需要较长的操作时间,操作系统为了充分利用cpu,此时会暂停到当前的I/O线程对CPU的控制(故又称同步式为阻塞式I/O),把cup资源然给其他的线程资源,当I/O线程完成了操作时,此时操作系统会恢复此时的I/O线程,从而当前I/O线程重新获得了cup的的控制权,继续完成其他操作。
var fs = require("fs");
//readFileSync表示同步读取
var data = fs.readFileSync("text.js","utf-8");
console.log(data);
console.log("end");
针对所有I/O操作不采用阻塞策略,当线程遇到I/O操作时,不会以阻塞的方式等待I/O操作的完成或数据的返回,而只是讲IO请求发送给操作系统,继续执行下一条语句,当操作系统完成IO操作时,以事件的形式通知执行IO操作的线程,线程会在特定时候处理这个事件,为了 处理异步IO,线程必须有事件循环,不断的检查有没有未处理的事件,依次予以处理。
var fs = require("fs");
//默认是采用异步式I/O
fs.readFile('text.js','utf-8',function(err,data){
if(err){
console.log(err);
}else{
console.log(data);
}
})
console.log("end");
输出的结果是:
end
read me //text.js文件里面的内容
异步式:一个线程永远在执行计算操作,这个线程所使用的CPU核心利用率永远是100%,IO以事件的方式通知。
同步式:多线程往往能提高系统吞吐量,因为一个线程阻塞还有其他线程在工作,多线程可以让CPU资源不被阻塞中的线程浪费(吞吐量:指于一通讯通道上单位时间能成功传递的平均资料量。简单的说就是表示系统能处理的多快)
这样就很明显虽然同步式的效率就不如异步式因为同步式会浪费事件在阻塞恢复当中。
ID | 同步式IO(阻塞式) | 异步式IO(非阻塞) |
---|---|---|
1 | 利用多线程提供吞吐量 | 单线程即可实现高吞吐量 |
2 | 通过事件片分割和线程调度利用多核CPU | 通过功能划分利用多核 |
3 | 需要由操作系统调度多线程使用多核CPU | 可以将单线程绑定到单核CPU |
4 | 难以充分利用CPU资源 | 可以充分利用CPU资源 |
5 | 内存轨迹大,数据局部性弱 | 内存轨迹小,数据局部性强 |
6 | 符合线性的编程思维 | 不符合传统编程思维 |
下面来解释几个不好理解的:
先理解什么是调度,
调度:简单来说就是指定两个参数工作
和触发器
,比如吃饭在五分钟后进行,吃饭就是工作,五分钟就是触发器
这个的前提是已经安装了node环境之后的;
打一个一个文本编辑器,在其中输入
console.log('Hello World');
并保存为helloworld.js。打开dos窗口进入该文件的目录运行
node helloworld.js
执行则可以看到输出的helloworld
node -v 版本
node -e eval scipt eval(“console.log('呵呵')”);
例:node -e "console.log('hello world')"; 直接执行
node 直接进入编译模式
console.log("111")
第一行是输出、第二行是返回值
var http = require('http');
http.createServer(function(request,reponse){
//回送一个head头部,返回值是200
reponse.writeHead(200,{'Content-Type':'text/html'});
//内容信息
reponse.write("thisd i strue");
//请求结束
reponse.end();
}).listen(5353);//监听的是5353端口
console.log("end");
页面上的显示图如下
对应于上面的创建的服务器,如果每一改动都要手动的运行一次这样肯定是会很麻烦的,我们可以安装一个插件可以不用每次都重新运行一次服务器
npm install -supervisor -g
要把这个插件安装到全局
然后通过 supervisor XX.js可以监听变化
EventEmitter是Node.js中事件的核心对象,所有的事件基本都是通过这个对象完成构建的!
var eventEmit = require('events').EventEmitter;
var event = new eventEmit();
//定义一个事件
event.on('newEvent',function () {
console.log("this is zidingyi event");
});
//通过emit方法来触发这个事件
event.emit("newEvent");
这也是最重要的一点,说到事件循环就要说说JavaScript的运行机制了,JavaScript是一门单线程的语言,单线程的意思就是每次只能执行一个任务,那么当遇到多个任务怎么办呢?还能怎么办排队呗!
但是如果任务多的话或者运行耗时的人任务,排队这种方法就会在网页中出现“假死”的情况,因为JavaScript一直停不下来。
如果遇到了某个任务十分耗费资源,比如说涉及到多个I/O操作的时候,运用”排队”的原理(没此运行完一个才能运行下一个):从第一个开始每次开始任务请求(比如:http请求,文件读取请求)的时候,就要等到文件回应之后才能执行下一个任务,在这里等待文件回应相对于任务请求就是很耗费时间的[1],就好像下面这个图一样:
因为I/O操作操作慢,大多数的事件都是等待I/O操作的返回结果。
这是Node出现的出现的原因,Node中是基于事件循环
的。官网对事件循环的定义是这样的:a programming construct that waits for and dispatches events or messages in a program
大致的意思就是在程序中设置两个进程,一个是负责程序本身的主进程
,另一个是负责主进程
和其他进程之间的通信
(就像上图中的http请求,文件请求等I/O操作)。
说的明白一点就是上面的图中的操作是辅助进程
做的事情,每当遇到I/O操作的时候,主线程就会让事件循环去通知响应的I/O程序,然后字节接着往下进行,等到I/O程序操作完成,事件循环在把结果返回主进程。自然而然就不会存在等待时间了。
这个机制就相当于下面的图(画的好难看,勿喷):
这也就是Node实现的非阻塞式I/O
这是Node的一个系列,可以在本栏目下看其他关于Node文章,会 一直更新,有问题请在下方留言