Skip to content

Latest commit

 

History

History
397 lines (298 loc) · 8.37 KB

01.node核心概览.md

File metadata and controls

397 lines (298 loc) · 8.37 KB

回调函数

快速理解

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.多个异步任务并发的情况下,如果希望多个结果都返回再进行下一步操作,会缺乏有效的处理方式,即便使用全局计数器也不是一个好的方式

node.js原生HTTP服务

快速实现一个服务器

// 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)

Buffer 处理二进制流

快速上手

// 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>

使用protocol-buffers对二进制进行编解包

// 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>

RPC调用

RPC调用与 Ajax 的区别

1.RPC 一般不用 DNS 寻址服务,因为一般用在内网相互请求,即服务器之间相互请求,通常使用统一的标识符通过一个寻址服务器得到 IP;Ajax 需要使用 DNS服务器 进行寻址,获得目标服务器域名

2.RPC 应用层协议一般不使用 HTTP,而使用二进制协议获得性能优势,比如更小的数据包体积,更快的解码效率;Ajax 是一个文本协议,主要是 HTML 或者 JSON

3.RPC 基于 TCP 或 UPD,利用 TCP 时经常是半双工通信或全双工通信

使用net模块搭建单工通信

快速上手:

服务端

//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))

多路复用的RPC 通道(全双工)

全双工通道:

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',
}

多进程

child_process

// 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');

cluster

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')
}