node.js的回调函数定义规范是错误优先,err
为第一个参数,callback
回调函数为第二个参数
这是为什么呢?因此错误处理在node里是非常重要的,看下面的代码
//03.js
function fn(callback){
if(Math.random()<0.5){
callback('success')
}else{
throw new Error(new Error('error'))
}
}
try {
fn(()=>{
console.log('success')
})
} catch (error) {
console.log('err',error)
}
会发现fn函数虽然是包裹在try...catch中执行的,但是错误却没有被捕获到,而是将错误抛到了全局,导致了整个程序的失败。之所以错误没有被捕获,是因为node里每一个事件循环都是一个独立的调用栈,而try...catch与fn函数是在不同的调用栈,因此错误没有被外面try catch
捕获
所以我们需要给每一个函数做错误操作,以捕获可能出现的错误
我们可以用下面的方式来进行错误捕获:
function fn(callback){
if(Math.random()<0.5){
callback('success')
}else{
callback(new Error('error'))
}
}
fn((res)=>{
if(res) return console.log('error')
console.log('success')
})
node.js规范了对错误的处理,即将第一个参数作为err,如果第一个参数不为空,则判断为失败
1.多层嵌套会形成回调地狱
2.多个异步任务并发的情况下,如果希望多个结果都返回再进行下一步操作,会缺乏有效的处理方式,即便使用全局计数器也不是一个好的方式
快速实现一个服务器
// 05.js
const http = require('http');
http.createServer((req, res)=>{
res.writeHead(200);
res.end('http');
}).listen(3000)
支持网页服务
// 06.js
const http = require('http')
const fs = require('fs')
http.createServer(function(req,res){
res.writeHead(200)
fs.createReadStream(`${__dirname}/HTML/01.html`).pipe(res)
}).listen(3000)
// 07.js
const buffer = Buffer.from('fltenwall');
const buffer2 = Buffer.from([1,2,3,4]);
const buffer3 = Buffer.alloc(20);
console.log(buffer)
console.log(buffer2)
console.log(buffer3)
/*
<Buffer 66 6c 74 65 6e 77 61 6c 6c>
<Buffer 01 02 03>
<Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>
*/
buffer2.writeInt8(12,1)
console.log(buffer2) // <Buffer 01 0c 03 04>
buffer2.writeInt16BE(512,2)
console.log(buffer2) // <Buffer 01 0c 02 00>
buffer2.writeInt16LE(512,2)
console.log(buffer2) // <Buffer 01 0c 00 02>
// Codes/Node/Programe/Buffer-protocal/index.js
const fs = require('fs');
const protobuf = require('protocol-buffers')
const messages = protobuf(fs.readFileSync(__dirname+'/01.proto'), 'utf-8')
console.log(messages);
/*
Messages {
Person: {
type: 2,
message: true,
name: 'Person',
buffer: true,
encode: [Function: encode] { bytes: 0 },
decode: [Function: decode] { bytes: 0 },
encodingLength: [Function: encodingLength],
dependencies: [ [Object], [Object] ]
}
}
*/
const buf = messages.Person.encode({
age: 12,
name: 'fltenwall'
})
console.log(buf) // <Buffer 80 01 0c 02 09 66 6c 74 65 6e 77 61 6c 6c>
1.RPC 一般不用 DNS 寻址服务,因为一般用在内网相互请求,即服务器之间相互请求,通常使用统一的标识符通过一个寻址服务器得到 IP;Ajax 需要使用 DNS服务器 进行寻址,获得目标服务器域名
2.RPC 应用层协议一般不使用 HTTP,而使用二进制协议获得性能优势,比如更小的数据包体积,更快的解码效率;Ajax 是一个文本协议,主要是 HTML 或者 JSON
3.RPC 基于 TCP 或 UPD,利用 TCP 时经常是半双工通信或全双工通信
快速上手:
服务端
//Codes/Node/Programe/net/01/server.js
const net = require('net');
const server = net.createServer(socket=>{
socket.on('data', function(buffer){
console.log(buffer.toString())
})
})
server.listen(3001)
客户端
//Codes/Node/Programe/net/01/client.js
const net = require('net');
const socket = new net.Socket({});
// 手动建立与服务器的连接
socket.connect({
host: '127.0.0.1',
port: 3001,
})
setInterval(function(){
socket.write('fltenwall');
},1000)
服务端
// Codes/Node/Programe/net/02/server.js
const net = require('net');
const server = net.createServer(socket=>{
socket.on('data', function(buffer){
const id = buffer.readInt16BE();
setInterval(function(){
socket.write(Buffer.from(data[id]))
},1000)
})
})
server.listen(3001)
const data = {
1:'java',
2:'javascript',
3:'python',
}
客户端
// Codes/Node/Programe/net/02/client.js
const net = require('net');
const socket = new net.Socket({});
// 手动建立与服务器的连接
socket.connect({
host: '127.0.0.1',
port: 3001,
})
const list = [1,2,3];
let buffer = Buffer.alloc(2);
let index = Math.floor(Math.random()*list.length);
buffer.writeInt16BE(list[index])
socket.write(buffer)
socket.on('data', function(buffer){
console.log(list[index],buffer.toString());
index = Math.floor(Math.random()*list.length);
socket.write(encode(index))
})
function encode(index){
buffer = Buffer.alloc(2);
buffer.writeInt16BE(list[index]);
return buffer;
}
socket.write(encode(index))
全双工通道:
1.应用层协议需要有标记包的字段
2.处理粘包、不完整包需要有标记包长的字段
3.错误处理
客户端
// Codes/Node/Programe/net/03/client.js
const net = require('net');
const socket = new net.Socket({});
// 手动建立与服务器的连接
socket.connect({
host: '127.0.0.1',
port: 3001,
})
const list = [1000,2000,3000];
let index = Math.floor(Math.random()*list.length);
socket.on('data', function(buffer){
const seqBuffer = buffer.slice(0,2);
const titleBuffer = buffer.slice(2);
console.log(seqBuffer.readInt16BE(),titleBuffer.toString());
socket.write(encode(index))
})
let seq = 0;
function encode(index){
buffer = Buffer.alloc(6);
buffer.writeInt16BE(seq);
buffer.writeInt32BE(list[index],2);
console.log(seq,list[index])
seq++;
return buffer;
}
setTimeout(function(){
socket.write(encode(index))
},50)
服务端
// Codes/Node/Programe/net/03/server.js
const net = require('net');
const server = net.createServer(socket=>{
socket.on('data', function(buffer){
const seqBuffer = buffer.slice(0,2);
const id = buffer.readInt32BE(2);
setTimeout(function(){
const buffer = Buffer.concat([seqBuffer,Buffer.from(data[id])])
socket.write(buffer);
},10+Math.random()*1000)
})
})
server.listen(3001)
const data = {
1000:'java',
2000:'javascript',
3000:'python',
}
// Codes/Node/Programe/child_process/01/master.js
const childProcess = require('child_process');
const child_process = childProcess.fork(__dirname + '/child.js');
// 给子进程发消息
child_process.send('father');
child_process.on('message', function(str){
console.log(str);
})
// Codes/Node/Programe/child_process/01/child.js
process.on('message', (str)=>{
console.log(str);
})
process.send('child');
node 压测:
-c50 50个并发 -n400 400次
ab -c50 -n400 http://127.0.0.1:3000
http服务代码
// Codes/Node/Programe/cluster/app.js
const http = require('http');
const fs = require('fs');
http.createServer((req,res)=>{
res.writeHead(200);
fs.createReadStream(__dirname + '/index.html').pipe(res)
}).listen(4000)
使用cluster实现多进程
// Codes/Node/Programe/cluster/index.js
const cluster = require('cluster');
const os = require('os');
// 判断是否是主进程
if(cluster.isMaster){
// 分发到其他子进程处理Http请求
for(let i=0;i<os.cpus().length / 2;i++){
cluster.fork()
}
}else{
require('./app.js')
}