脑图:https://www.w3cschool.cn/xkhotq/dy3tbozt.html
详解1:https://segmentfault.com/a/1190000005984425
详解2:https://segmentfault.com/a/1190000018850117
https://segmentfault.com/a/1190000018850135
https://juejin.cn/post/6844903605284274189#heading-91
七.网络编程
Node提供了net、dgrm、http、https这四个模块,分别用于处理TCP、UDP、HTTP、HTTPS,适用于服务器端和客户端。
1.构建TCP服务
应用层:HTTP,SMTP,IMAP等
表示层:加密/解密等
会话层:通信链接/维持会话
传输层:TCP/UDP
网络层:IP
链路层:网络特有的链路接口
物理层:网络物理硬件
// TCP是面向连接的协议,其显著特征是在传输之前需要3次握手形成会话
// 创建TCP服务器端来接受网络请求
var net = require('net')
var server = net.createServer()
server.on('connection', function (socket) {
console.log('connection')
})
server.listen(8000)
TCP服务的事件:
1.服务器事件
listening: 调用server.listen()绑定端口或者domain socket后触发,如server.listen()
connection: 每个客户端套接字连接到服务器端时触发,如net.createServer()
close: 当服务器关闭时触发
error: 当服务器异常时触发
2.连接事件
data: 当一端调用write()发送数据时,另一端会触发data事件
end: 任意一端发送FIN数据,另一端将会触发
connect: 客户端与服务器连接成功后,客户端触发该事件
drain: 当任意一端调用write()时,当前这端会触发该事件
error: 异常触发该事件
close: 当套接字完全关闭时,触发该事件
timeout: 当一定时间后,连接不活跃,将触发该事件,告知当前用户,该连接已经被闲置了
2.构建UDP服务
UDP的定义:用户数据包协议
应用:音频、视频、dns服务
1) 创建UDP套接字
var dgram = require('dgram');
var socket = dgram.createSocket("udp4")
2)创建UDP服务器端
调用dgram.bind(port,[address])对网卡和端口进行绑定即可
3)创建UDP客户端
socket.send(要发送的buf, buf的偏移, buf长度, port, address, [callback])
UDP服务的事件
message: 当UDP套接字侦听网卡端口后,接收到消息时触发该事件(触发携带的数据为消息Buffer对象和一个远程地址信息)
listening: UDP开始监听时,触发该事件
close
error
3.构建HTTP服务
1) http
初识HTTP: 超文本传输协议,构建在TCP协议之上, 属于应用层协议(B/S模式,即客户端/服务器)
HTTP报文:
采用curl工具,查看这次网络通信的所有报文信息。报文分为四部分
$ curl -v http://127.0.0.1:1337
//第一部分:是经典的TCP三次握手,这样就建立了连接
* About to connect() to 127.0.0.1 port 1337 (#0)
* Trying 127.0.0.1...
* connected
* Connected to 127.0.0.1 (127.0.0.1) port 1337 (#0)
//第二部分:在完成握手之后,客户端向服务器端发送请求报文。
> GET / HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5
> Host: 127.0.0.1:1337
> Accept: */*
//第三部分:服务器端完成处理后,向客户端发送的响应内容,包括响应头和响应体。
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Date: Sat, 06 Apr 2013 08:01:44 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
Hello World
第四部分:结束会话的信息
* Connection #0 to host 127.0.0.1 left intact
* Closing connection #0
2)http模块
http模块将连接所用的套接字的读写抽象为ServerRequest和ServerResponse对象
1.浏览器,是一个HTTP代理,用户的行为将会通过它转化为HTTP请求报文发送给服务端,
服务端处理请求后,发送响应报文给代理,代理在解析报文后,将用户需要的内容呈现在界面上。
2.TCP服务以connection为单位进行服务,HTTP服务以request为单位进行服务。
http是将connection到request进行了封装
1.http服务端事件
connection - 在http请求前,建立tcp时触发
request,当请求数据发送到服务端,在解析出http请求头后触发
close,当tcp连接断开
checkContinue,和request事件互斥。当客户端在发送较大数据的时候,并不会将数据直接发送,而是先发送一个头部带Expect:100-continue的请求到服务器,这是服务器会触发checkContinue
connect, 当客户端发起CONNECT请求时触发,而发起CONNECT请求通常在http代理时出现。
upgrade,当客户端要求升级连接的协议时,需要和服务端协商
clientError,连接的客户端触发error事件,传递到服务端
2.http客户端事件
response:客户端在请求后得到服务端响应时触发
socket:当底层连接池中建立的连接分配给当前请求对象时触发
connect: 当客户端向浏览器发起CONNECT请求时,如果服务器端响应了200状态码,客户端会触发该事件
upgrade,客户端向服务器发起upgrade请求时,如果服务端响应了101 switching protocol状态
continue,客户端向服务端发起Expect:100-continue以试图发送大数据量
4.构建WebSocket服务
5.网络服务与安全
八. 构建web
前后端JS统一的好处:
无需切换语言环境,在跨域时候,带来额外的好处
数据json可以很好的实现跨前后端直接使用
一些业务,如模版渲染不需要纠结在前端还是后端进行
1.基础功能 2.数据上传 3.路由解析 4.中间件 5.页面渲染
九. 进程
两个问题:
如何让node充分利用多核cpu服务器?
异常的捕获, 如何保证node进程的健壮性和稳定性?
1.服务模型的变迁
石器时代:同步-基本淘汰
青铜时代:复制-每个进程需要一个进程来服务(内存浪费)
白银时代:多线程-一个线程服务一个请求,并且之间共享数据
黄金时代:事件驱动-所有处理都在单线程上面进行
2.多进程架构
node提供了child_process模块,并提供了child_process.fork()函数来实现进程的复制
1)创建子进程
spawn() - 启动一个子进程来执行命令
exec() - 同上,但有一个回调函数获取子进程状况
execFile() - 启动一个子进程来执行可执行文件
fork() - 与spawn()类似,只需执行js文件模块即可
2)进程间通信 IPC(Inter-Process Communication)
1. master-worker模式中,实现主进程管理和调度需要主从进程中通信
2.IPC进程间通信 - 为了让不同进程能够互相访问资源并进程协调工作
如命名管道、匿名管道、socket、信号量、共享内存、消息队列、Domain、Socket等,
node中实现IPC通道的是管道技术(pipe)
双向通信,在系统内核中完成通信,不用经过实际的网络层
原理:子进程对象则由send()方法实现主进程向子进程发送数据,message事件实现收听子进程发来的数据。
(通过消息传递,而不是共享或直接操纵相关资源,这是较为轻量和无依赖的做法)
// parent.js
var cp = require('child_process')
var n = cp.fork(__dirname + '/sub.js')
n.on('message', function (m) {
console.log('PARENT got message:', m)
})
n.send({ hello: 'world' })
3.句柄传递
send()方法除了能够通过IPC发送数据外还能发送句柄(第2参数)
句柄 - 是一种可以用来标识资源的应用,他的内部包含了对象的文件描述符
3.集群稳定之路
通过基础技术,用child_process模块正在单机上搭建Node集群是件容易的事情
(在多核CPU环境下 ,让Node进程能够充分利用资源不再是难题)
1)进程事件
error - 当子进程无法被复制创建,无法被杀死,无法发送消息时触发
exit - 子进程退出时触发
close - 终止时触发该事件
disconnect - 关闭IPC通道
2)自动重启
3)负载均衡
node默认提供的机制是采用操作系统的抢占式策略
新的策略是轮叫调度。工作方式是由主进程接受连接,将其一次分发给工作进程
4)状态共享
在多个进程之间共享数据
第三方数据存储
实现同步:子进程向第三方进行定时轮训
主动通知
主动通知子进程,轮训
4.cluster模块
引入cluster,解决多核cpu的利用率问题
原理:cluster模块就是child_process和net模块的组合应用。在fork子进程时,将socket的文件描述符发送给工作进程。通过so_reuseaddr端口重用,从而实现多个子进程共享端口。
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`主进程 ${process.pid} 正在运行`);
// 衍生工作进程。
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('listening', () => {
console.log('listening')
})
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 已退出`);
});
} else {
// 工作进程可以共享任何 TCP 连接。
// 在本例子中,共享的是一个 HTTP 服务器。
http.createServer((req, res) => {
res.writeHead(200);
res.end('你好世界\n');
}).listen(8000);
console.log(`工作进程 ${process.pid} 已启动`);
}
process.on('exit', () => {
console.log('exit')
})
十. 测试
1.单元测试
1.断言:一阶逻辑(真假),目的是标示开发者预期的结果(错误中止或出现错误信息)如: assert.equal()
2.测试框架
如:优秀测试框架mocha
用 describe和it 进行组织,describe描述多层级的结构,it具体到测试用例
describe('Array', function(){
before(function(){
// ...
});
describe('#indexOf()', function(){
it('should return -1 when not present', function(){
[1,2,3].indexOf(4).should.equal(-1)
})
})
})
3.测试代码的文件组织
4.测试用例
断言、正向测试、反向测试、异步测试、超时设置
5.测试覆盖率
统计每一行代码是否可执行(jscover模块)
6.mock
通过伪造调用方法测试上次代码的健壮性
7.持续集成
通过一定工具来实现 工程化和自动化(makefile和travis-ci)
2.性能测试
1.基准测试
统计多少时间执行了多少次某个方法
2.压力测试
判断网络接口的性能,服务器的并发处理能力(指标有吞吐率,响应时间和并发数)
常用工具:ab、siege、http_load
十一. 产品化
1.项目工程化
目录结构、
构建工具、
编码规范、
代码审查
2.部署流程
部署环境:
代码 --> stage测试环境 --> pre-release预发布环境 --> product生产环境
部署操作:
bash脚本,获取进程ID。重启,中断,启动
3.性能
1)动静分离
静态请求(如图片,脚本,多媒体等)使用Nginx或CDN静态文件服务器;
动态请求用node(web应用)
2)启用缓存
提升服务速度,避免不必要的计算(常用redis或memcached)
3)多进程架构
充分利用多核CPU(如cluster模块,pm2模块等)
4)读写分离
对数据库进行主从设计,这样读取数据操作不再受到写入的影响,降低了性能的影响
4.日志
1)访问日志
记录每个客户端对应用的访问
在web应用中,主要记录http请求中的关键数据
日志中间件-框架connect
2)异常日志
node提供的console对象
log和info方法将信息输出给标准输出process.stdout; warn和error方法将信息输出到标准错误process.stderr
3)日志与数据库
将日志分析和日志记录分离开(日志离线分析工具)
4)分割日志
按日期分割
5.监控报警
1)日志监控
监控异常日志 - 文件的变动,将新增的异常按异常类型和数量反应出来
监控访问日志 - 体现业务qps值,pv/uv,预知访问高峰
2)响应时间
异常或者性能瓶颈,会导致系统的响应时间变长
3)进程监控
工作进程的数量, 如果低于预估值,应当发出报警
4)磁盘监控
监控磁盘的用量,设置警戒值
5)内存监控
内存只升不降,有内存泄漏
6)cpu占用监控
cpu分为内核态,用户态,iowait等
用户态占用高: 服务器上应用大量cpu开销
内核态占用高:服务器花费大量时间进程调度或者系统调用
7)cpu load监控(cpu平均负载)
描述操作系统当前的繁忙程度
8)i/o负载
反应磁盘读写情况
9)网络监控
10)应用状态监控
11)dns监控
报警系统:邮件报警和短信报警
6.稳定性
多机器,多机房,容灾备份等