第三章 Node.js快速入门
3.1.3 建立HTTP服务器
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<h1>Node.js</h1>');
res.end('<p>Hello World</p>');
}).listen(3000);
console.log("HTTP server is listening at port 3000.");
3.2 异步式I/O与事件式编程
node的特点是异步编程式I/O(非阻塞),通过事件和回调来组织。
3.2.1 阻塞和线程
阻塞(同步式I/O):遇到硬盘读写或网络通讯(即I/O操作)停止线程的cpu控制权,让给其他的线程。等待I/O操作结束恢复cpu控制权,之后继续运行。
非阻塞(异步式I/O):遇到I/O操作发送给操作系统,继续运行下一条语句。当完成后以事件形式通知回去。线程必须有事件循环,不断检查没有处理的事件,依次处理。
阻塞只能一次处理一项任务。提高吞吐量只能通过多线程。非阻塞单线程即可胜任,他核心利用率会保持100%。
阻塞模式下多线程可以让 CPU 资源不被阻塞中的线程浪费。
而在非阻塞模式下,线程不会被 I/O 阻塞,永远在利用 CPU。多线程带来的好处仅仅是在多核 CPU 的情况下利用更多的核,而Node.js的单线程也能带来同样的好处。
单线程的好处:既然阻塞和非阻塞效果相同,那么为什么非要选择非阻塞+单线程呢?
因为操作系统创建线程是昂贵的,需要分配内存、列入调度,同时在线程切换的时候还要执行内存换页, CPU 的缓存被
清空,切换回来的时候还要重新从内存中读取信息,破坏了数据的局部性。 而单线程根本没这些烦恼。大大节约性能。
总结:如果一个工作的I/O操作远大于计算操作,那么非阻塞+单线程性能好很多。而web服务都是I/O操作很多的。
3.2.2 回调函数
使用回调函数实现异步。
var fs = require('fs');
//异步方式
fs.readFile('test.html','utf-8',(err,data)=>{
if(err){
console.log(err);
}else{
console.log(data);
}
})
//同步方式
let data = fs.readFileSync('test.html','utf-8');
console.log(data);
3.2.3 事件
node.js所有异步I/O操作都会在完成时发送到事件队列,前面的fs.readFile和http.createServer都是通过eventEmitter实现的。
let Events = require('events');
let event = new Events.EventEmitter();
event.on('e',function(){
console.log('a');
})
setTimeout(() => {
event.emit('e');
}, 1000);
3.3 模块和包
模块(module)和包(package)是node.js的最重要的支柱。它用于拆分,封装,组合。
node.js模块基本遵从CommonJS标准。
3.3.1什么是模块?
模块是node.js程序基本组成部分,他可能是js代码,json或者编译的c++扩展。
3.3.2 创建模块
1.创建模块
使用exports公开接口,使用require获取。
exports只是module.exports的引用,如果你想要使用exports导出模块,千万不能给它赋值!
//m.js文件
var value = 42;
function sayHello() {
console.log(value);
}
function setValue(v) {
value = v;
}
exports.sayHello = sayHello;
exports.setValue = setValue;
//index.js
const m = request('./m');
m.sayHello(); //输出42
2.单次加载
request不会重复加载模块,无论调用多少次request加载的都是一个模块。
//index.js
var m1 = require('./m');
var m2 = require('./m');
m1.setValue(1);
m2.setValue(2);
m1.sayHello(); //输出2
由于书中的node.js版本太旧,3.34创建包和3.4调试已经不再适合现在的版本,所以不做记录。