Node.js 网络 HTTP、部署
课程目标
- 掌握计算机网络
OSI、TCP/IP基本模型; - 掌握并实战
TCP、UDP、HTTP协议的基本使用; - 从
0~1部署node环境;
课程大纲
OSI & TCP/IP模型设计TCP/UDPHTTPwebsocket- 从
0到1部署node.js及运维
OSI & TCP/IP 模型设计
Q:协议是什么?
A:明确定义每部分的作用、职责,类似于规范及约束;
OSI 七层模型
OSI 七层模型(Open System Interconnection)开放式的系统互联;
从上往下:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层;
应用层:(Application Layer)
- 功能是直接向用户提供服务,完成用户希望在网络上完成的各种工作;
- 文件服务、目录服务、文件传输服务(
FTP)、远程登录服务(Telnet)、电子邮件服务(E-mail)、打印服务、安全服务、网络管理服务、数据库服务、DNS服务etc; - 在应用层交互的数据单元称为报文;
- 主要能力:
- 用户接口:应用层是用户与网络,以及应用程序与网络间的直接接口,使得用户能够与网络进行交互式联系;
- 实现各种服务:该层具有的各种应用程序可以完成和实现用户请求的各种服务;
表示层:(Presentation Layer)
-
对来自应用层的命令和数据进行解释,对各种语法赋予相应的含义,并按照一定的格式传送给会话层。其主要功能是“处理用户信息的表示问题,如编码、数据格式转换和加密解密”;
-
主要能力:
-
数据格式处理:协商和建立数据交换的格式,解决各应用程序之间在数据格式表示上的差异;
-
数据的编码:处理字符集和数字的转换。例如由于用户程序中的数据类型(整型或实型、有符号或无符号等)、用户标识等都可以有不同的表示方式,因此,在设备之间需要具有在不同字符集或格式之间转换的功能;
-
压缩和解压缩:为了减少数据的传输量,这一层还负责数据的压缩与恢复;
-
数据的加密和解密:可以提高网络的安全性;
-
会话层:(Session Layer)
- 向两个实体的表示层提供建立和使用连接的方法。将不同实体之间的表示层的连接称为会话。因此会话层的任务就是组织和协调两个会话进程之间的通信,并对数据交换进行管理;
- 主要能力:
- 会话管理:允许用户在两个实体设备之间建立、维持和终止会话,并支持它们之间的数据交换;
- 会话流量控制:提供会话流量控制和交叉会话功能;
- 寻址:使用远程地址建立会话连接;
- 出错控制:从逻辑上讲会话层主要负责数据交换的建立、保持和终止,但实际的工作却是接收来自传输层的数据,并负责纠正错误。会话控制和远程过程调用均属于这一层的功能。但应注意,此层检查的错误不是通信介质的错误,而是磁盘空间、打印机缺纸等类型的高级错误;
传输层:(Transport Layer)
- 以下三层:数据通信,以上三层:数据处理,是通信子网和资源子网的接口和桥梁,起到承上启下的作用;
- 向用户提供可靠的端到端的差错和流量控制,保证报文的正确传输,向高层屏蔽下层数据通信的细节,即向用户透明地传送报文,常见的协议:
TCP、UDP; - 主要能力:
- 传输连接管理,在网络层的基础上为高层提供“面向连接”和“面向无接连”的两种服务;
- 处理传输差错。提供可靠的“面向连接”和不太可靠的“面向无连接”的数据传输服务、差错控制和流量控制。在提供“面向连接”服务时,通过这一层传输的数据将由目标设备确认,如果在指定的时间内未收到确认信息,数据将被重发;
- 监控服务质量;
网络层:(Network Layer)
- 通过路由选择算法,为报文或分组通过通信子网选择最适当的路径;
- 数据链路层的数据在这一层被转换为数据包,然后通过路径选择、分段组合、顺序、进/出路由等控制,将信息从一个网络设备传送到另一个网络设备;
- 主要能力:
- 寻址:数据链路层中使用的物理地址(如
MAC地址)仅解决网络内部的寻址问题。在不同子网之间通信时,为了识别和找到网络中的设备,每一子网中的设备都会被分配一个唯一的地址。由于各子网使用的物理技术可能不同,因此这个地址应当是逻辑地址(如IP地址); - 交换:规定不同的信息交换方式。常见的交换技术有:线路交换技术和存储转发技术,后者又包括报文交换技术和分组交换技术;
- 路由算法:当源节点和目的节点之间存在多条路径时,本层可以根据路由算法,通过网络为数据分组选择最佳路径,并将信息从最合适的路径由发送端传送到接收端;
- 连接服务:与数据链路层流量控制不同的是,前者控制的是网络相邻节点间的流量,后者控制的是从源节点到目的节点间的流量。其目的在于防止阻塞,并进行差错检测;
- 寻址:数据链路层中使用的物理地址(如
数据链路层:(Data Link Layer)
- 负责建立和管理节点间的链路;
- 通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路;
- 接受物理层的数据,封装成帧传给上层,接受上层数据解析成比特数据换发给物理层;
物理层:(Physical Layer)
- 实现计算机节点之间
bit流的透明传送; - 物理层的主要任务描述为确定与传输媒体的接口的一些特性,提供用于建立、保持和断开物理连接的机械的、电气的、功能的和过程的条件,也就是说物理层提供有关同步和比特流在物理媒体上的传输手段;
- 是物理硬件上的底层能力,比如光缆、电缆等设备连接形成组网;
- 包括信号的调制及信道复用等;
TCP/IP 协议
应用层
- 大多数普通与网络相关的程序为了通过网络与其他程序通信所使用的层。这个层的处理过程是应用特有的;数据从网络相关的程序以这种应用内部使用的格式进行传送,然后被编码成标准协议的格式;
- 应用层负责处理特定的应用程序细节,包括
Telnet(远程登录)、FTP(文件传输协议)、SMTP(简单邮件传送协议)以及SNMP(简单网络管理协议)等;
传输层
- 两台主机上的应用程序提供端到端的通信,有
2种传输协议:TCP(传输控制协议)和UDP(用户数据报协议);
网络层
- 处理分组在网络中的活动,例如分组的选路。网络层协议包括
IP协议(网际协议)、ICMP协议(Internet互联网控制报文协议),以及IGMP协议(Internet组管理协议);
网络接口层
- 也称作数据链路层,包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。一起处理与电缆(或其他任何传输媒介)的物理接口细节;
OSI 与 TCP/IP 协议间的关系
-
OSI是一种理论下的模型,而TCP/IP已被广泛使用,成为网络互联事实上的标准; -
OSI引入了服务、接口、协议、分层的概念,TCP/IP借鉴了OSI的这些概念建立TCP/IP模型; -
OSI先有模型,后有协议,先有标准,后进行实践;而TCP/IP则相反,先有协议和应用再提出了模型,且是参照的OSI模型;
【面试题】:OSI 与 TCP/IP 的区别?
OSI是理论上的模型,TCP/IP真实存在的;OSI先有的理论,再有的标准协议,TCP/IP反过来的;OSI是服务、分层的概览,TCP/IP借鉴的OSI思路;
每层间的合作与加工
Q:以下两种内容分别属于哪层模型?
HTML:应用层- 路由器:网络层
TCP & UDP
TCP、UDP 区别
TCP(Transmission Control Protocol),传输控制协议,是一种可靠、面向字节流的通信协议,把上面应用层交下来的数据看成无结构的字节流来发送,需要20个字节;TCP更能保证数据传输的稳定性和正确性;
UDP(User Datagram Protocol),用户数据包协议,是一个简单的面向数据报的通信协议,只是在其上面加上首部后就交给了下面的网络层,只占用8个字节(64 bit);(1字节 =8 bit)
| TCP | UDP | |
|---|---|---|
| 是否连接 | 面向连接 | 无连接 |
| 是否可靠 | 可靠传输,使用流量控制和拥塞控制 | 不可靠传输,不使用流量控制和拥塞控制 |
| 连接对象个数 | 只能是一对一通信 | 支持一对一,一对多,多对一和多对多交互通信 |
| 传输方式 | 面向字节流 | 面向报文 |
| 首部开销 | 首部最小 20 字节,最大 60 字节 | 首部开销小,仅 8 字节 |
| 适用场景 | 适用于要求可靠传输的应用,例如文件传输 | 适用于实时应用(IP 电话、视频会议、直播等) |
Q:直播属于哪种协议?
- 直播使用的
RTMP、HLS都是基于TCP的;- 不是所有流媒体通信都是
UDP;
TCP 的三次握手、四次挥手
三次握手
若客户端和服务端一起发送消息?
四次挥手
Q:
TCP是否每次连接都需要3次握手?
TCP快速打开(TCP Fast Open, TFO)- 第一次握手:
SYN在fast open带上cookie;- 服务端产生
cookie,在ACK返回改cookie给客户端;- 客户端存储
cookie;- 以后:
- 客户端带有
cookie;- 服务端验证该
cookie
- 有效,在返回
ACK的同时,将数据直接返回给应用层;- 无效,
3次握手;- 少一次
ACK的RTT(round-trip time,往返时延);Q:为什么最后要等
2个MSL(Maximum Segment Lifetime,最长报文寿命)
- 确保接收方收到
ACK;- 确保没有新的报文到发送方;
常见的 TCP 攻击方式
半连接 & 全连接
半连接:在三次握手中,当客户端发送 SYN 到服务端,服务端收到以后回复 ACK 和 SYN,状态由 LISTEN 变为 SYN_RCVD,此时这个连接就被推入了 SYN 队列,也就是半连接队列。
全连接:当客户端返回 ACK,服务端接收后,三次握手完成,这个时候连接等待被具体的应用取走,在被取走之前,它会被推入另外一个 TCP 维护的队列,也就是全连接队列。
SYN flood 攻击(半连接攻击)
客户端短时间内大量发送 SYN:
- 服务端的大量返回
ACK,半连接池吃紧; - 接受不到客户端的
ACK,服务端超时重发;
如何解决?
- 服务端加大半连接池;
SYN cookie:在接受达到SYN后不立即分配资源,根据这个SYN算出一个cookie,在ACK中返回给客户端,客户端在ACK里加上cookie,校验合法后分配资源;
TCP 的流量控制与拥塞控制
流量控制
TCP 报文有窗口大小字段,接收方返回 ACK 报文时会带上窗口大小,发送方的发送窗口根据接收方的窗口大小调节发送窗口,接受窗口为 0 等待,并启动计时器,若计时器为 0,则询问接受窗口的大小,继续为 0 等待,无反响重发报文。
拥塞控制
慢启动,拥塞避免
拥塞窗口 cwnd
默认为 1,指数增长,慢启动阈值:ssthresh
快重传:收到 3 个 ACK 就重传报文;
快恢复:将 cwnd 设置为 ssthresh 减半后的值,然后执行拥塞避免算法;
Q:
TCP是如何保证keep-alive的?客户端端等待超过一定时间后自动给服务端发送一个空的报文,如果对方回复了这个报文证明连接还存活着,如果对方没有报文返回且进行了多次尝试都是一样,那么就认为连接已经丢失,客户端就没必要继续保持连接了。如果没有这种机制就会有很多空闲的连接占用着系统资源。
Node 创建 TCP & UDP
TCP
tcp-server.js
/**
* 通过 net.Server 类来创建一个 TCP 服务器
*/
// 引入 net 模块,node 内置模块
var net = require('net');
// 实例化一个服务器对象
var server = new net.Server();
// 监听 connection 事件
server.on('connection', function (socket) {
console.log('someone connects');
var address = server.address();
var message = 'the server address is: ' + JSON.stringify(address);
// 发送数据;
socket.write(message, function () {
var writeSize = socket.bytesWritten;
console.log(message + ', has send');
console.log('the size of message is: ' + writeSize);
});
// 监听data事件;
socket.on('data', function (data) {
console.log(data.toString());
var readSize = socket.bytesRead;
console.log('the size of data is: ' + readSize);
});
});
// 设置监听端口
server.listen(8000);
// 设置监听时的回调函数
server.on('listening', function () {
const serverAddress = server.address();
const { port, address, family } = serverAddress;
console.log('Create server on http://127.0.0.1:8000/');
// TCP 服务器监听的端口号
console.log('the port of server is: ' + port);
// TCP 服务器监听的地址, :: 指本地
console.log('the address of server is: ' + address);
// 说明 TCP 服务器监听的地址是 IPv6 还是 IPv4
console.log('the family of server is: ' + family);
});
// 等同于
/*
server.listen(8000, function () {
console.log('Creat server on http://127.0.0.1:8000/');
});
*/
// 设置关闭时的回调函数
server.on('close', function () {
console.log('server closed!');
});
// 设置错误时的回调函数
server.on('error', function (err) {
console.log('error!');
});
tcp-client.js
/**
* 构建 TCP 客户端
*/
// 引入 net 模块
var net = require('net');
// 创建 TCP 客户端
var client = net.Socket();
// 设置连接的服务器
client.connect(8000, '127.0.0.1', function () {
console.log('connect the server');
// 向服务器发送数据
client.write('message from client');
});
// 监听服务器传来的 data 数据
client.on('data', function (data) {
console.log('the data of server is: ', data.toString());
});
// 监听 end 事件
client.on('end', function () {
console.log('data end');
});
UDP
udp-server.js
var dgram = require('dgram');
var server = dgram.createSocket('udp4');
var PORT = 33333;
var HOST = '127.0.0.1';
server.on('listening', function () {
var address = server.address();
console.log('UDP Server listening on ' + address.address + ':' + address.port);
});
server.on('message', function (message, remote) {
console.log(remote.address + ':' + remote.port + ' - ' + message);
});
server.bind(PORT, HOST);
udp-client.js
var dgram = require('dgram');
var message = Buffer.from('xianzao is here');
var PORT = 33333;
var HOST = '127.0.0.1';
var client = dgram.createSocket('udp4');
client.send(message, PORT, HOST, function (err, bytes) {
if (err) throw err;
console.log('UDP message sent to ' + HOST + ':' + PORT);
client.close();
});
udp-broadcast-server.js
var dgram = require('dgram');
var server = dgram.createSocket('udp4');
var port = 33333;
server.on('message', function (message, rinfo) {
console.log('server got message from: ' + rinfo.address + ':' + rinfo.port);
});
server.bind(port);
udp-broadcast-client.js
var dgram = require('dgram');
var client = dgram.createSocket('udp4');
var msg = Buffer.from('xianzao is broadcast here');
var port = 33333;
var host = '255.255.255.255';
client.bind(function () {
client.setBroadcast(true);
client.send(msg, port, host, function (err) {
if (err) throw err;
console.log('msg has been sent');
client.close();
});
});
HTTP
HTTP 是一种能够获取如 HTML 这样的网络资源的 protocol(通讯协议)。它是在 Web 上进行数据交换的基础,是一种 client-server 协议,也就是说,请求通常是由像浏览器这样的接受方发起的。一个完整的 Web 文档通常是由不同的子文档拼接而成的,像是文本、布局描述、图片、视频、脚本等等。
HTTP 被设计于 20 世纪 90 年代初期,是一种可扩展的协议。它是 应用层的协议,通过 TCP,或者是TLS-加密的 TCP 连接来发送,理论上任何可靠的传输协议都可以使用。因为其良好的扩展性,时至今日,它不仅被用来传输超文本文档,还用来传输图片、视频或者向服务器发送如 HTML 表单这样的信息。HTTP 还可以根据网页需求,仅获取部分 Web 文档内容更新网页。
HTTP 组件系统
HTTP 是一个 client-server 协议:请求通过一个实体被发出,实体也就是用户代理。大多数情况下,这个用户代理都是指浏览器,当然它也可能是任何东西,比如一个爬取网页生成维护搜索引擎索引的机器爬虫。
每一个发送到服务器的请求,都会被服务器处理并返回一个消息,也就是 response。在这个请求与响应之间,还有许许多多的被称为 proxies 的实体,他们的作用与表现各不相同,比如有些是网关,还有些是 caches 等。
客户端:user-agent
user-agent 就是任何能够为用户发起行为的工具。这个角色通常都是由浏览器来扮演。一些例外情况,比如是工程师使用的程序,以及 Web 开发人员调试应用程序。
浏览器总是作为发起一个请求的实体,他永远不是服务器(虽然近几年已经出现一些机制能够模拟由服务器发起的请求消息了)。
要展现一个网页,浏览器首先发送一个请求来获取页面的 HTML 文档,再解析文档中的资源信息发送其他请求,获取可执行脚本或 CSS 样式来进行页面布局渲染,以及一些其它页面资源(如图片和视频等)。然后,浏览器将这些资源整合到一起,展现出一个完整的文档,也就是网页。浏览器执行的脚本可以在之后的阶段获取更多资源,并相应地更新网页。
一个网页就是一个超文本文档。也就是说,有一部分显示的文本可能是链接,启动它(通常是鼠标的点击)就可以获取一个新的网页,使得用户可以控制客户端进行网上冲浪。浏览器来负责发送 HTTP 请求,并进一步解析 HTTP 返回的消息,以向用户提供明确的响应。
Web 服务端
在上述通信过程的另一端,是由 Web Server 来服务并提供客户端所请求的文档。Server 只是虚拟意义上代表一个机器:它可以是共享负载(负载均衡)的一组服务器组成的计算机集群,也可以是一种复杂的软件,通过向其他计算机(如缓存,数据库服务器,电子商务服务器 ...)发起请求来获取部分或全部资源。
代理(Proxies)
在浏览器和服务器之间,有许多计算机和其他设备转发了 HTTP 消息。由于 Web 栈层次结构的原因,它们大多都出现在传输层、网络层和物理层上,对于 HTTP 应用层而言就是透明的,虽然它们可能会对应用层性能有重要影响。还有一部分是表现在应用层上的,被称为代理(Proxies)。代理(Proxies)既可以表现得透明,又可以不透明(“改变请求”会通过它们)。代理主要有如下几种作用:
- 缓存(可以是公开的也可以是私有的,像浏览器的缓存)
- 过滤(像反病毒扫描,家长控制...)
- 负载均衡(让多个服务器服务不同的请求)
- 认证(对不同资源进行权限管理)
- 日志记录(允许存储历史信息)
HTTP 特点
HTTP是简单的
虽然下一代 HTTP/2 协议将 HTTP 消息封装到了帧(frames)中,HTTP 大体上还是被设计得简单易读。HTTP 报文能够被人读懂,还允许简单测试,降低了门槛,对新人很友好。
- HTTP 是无状态的,有会话的
HTTP 是无状态的:在同一个连接中,两个执行成功的请求之间是没有关系的。这就带来了一个问题,用户没有办法在同一个网站中进行连续的交互,比如在一个电商网站里,用户把某个商品加入到购物车,切换一个页面后再次添加了商品,这两次添加商品的请求之间没有关联,浏览器无法知道用户最终选择了哪些商品。而使用 HTTP 的头部扩展,HTTP Cookies 就可以解决这个问题。把 Cookies 添加到头部中,创建一个会话让每次请求都能共享相同的上下文信息,达成相同的状态。
注意,HTTP 本质是无状态的,使用 Cookies 可以创建有状态的会话。
HTTP 结构
起始行+头部+空行+实体 请求报文:方法+path+HTTP 版本号 响应报文:版本号+状态码+原因
HTTP 方法 GET POST HEAD PUT DEDLETE OPTIONS : 获取目标资源所支持的方式,用来跨域 TRACE:追踪响应-请求路径 CONNECT:建立隧道连接,用于代理服务器
GET/POST 区别: \1. 缓存 \2. 编码 GET url 编码 \3. 安全性 \4. 幂等
Node 服务端部署
服务器购入流程
-
选择合适的厂商,如国内的阿里云、腾讯云、国外的
AWS、Vultr等都可以,根据自己的需要选择。(下面以阿里云为例)- 尽量跟自己所在区域近一些,
response会快点; - 如果有自己搭的诉求,可以考虑海外站点或者国内厂商的海外服务器;
- 尽量跟自己所在区域近一些,
-
- 选择
ECS,建议买台便宜的自己学习; - 配置安全组:防火墙按序打开,如果防火墙开启,端口号也要按需配置;
查看防⽕墙状态 firewall-cmd --state 启动firewall: systemctl start firewalld 临时关闭firewall(设置开机⾃启后会打开) systemctl stop firewalld 永久关闭firewall systemctl disable firewalld 设置开机⾃启: systemctl enable firewalld 重启防⽕墙(每次操作后重启防⽕墙): systemctl restart firewalld 开端⼝命令:firewall-cmd --zone=public --add-port=80/tcp --permanent 所有IPV4 端⼝范围:-1/-1 源:0.0.0.0/0 SSH端⼝号 端⼝范围:22/22 源:0.0.0.0/0 远程连接端⼝ 端⼝范围:3389/3389 源:0.0.0.0/0- 域名:根据自己的需要,看是否要购买域名,若购买域名,需要将域名解析到自身的服务器上;
- 证书:购买
ECS后,可以免费生成一个证书,有效期一年;
- 选择
本地环境设置
主要配置 VScode,常规的开发环境不予补充。
VScode 插件
此处常规开发中的插件等不介绍了。
remote-SSH
使用 VScode 远程连接服务器。
地址:marketplace.visualstudio.com/items?itemN…
支持用户名 & 密码配置,和秘钥配置,建议使用后者。
配置内容:
本地配置入口
~/.ssh/config
// example
// 本地配置入口
Host xianzao
HostName 47.243.224.215
User root
IdentityFile ~/.ssh/id_rsa
// 服务器配置入口
~/.ssh/authorized_keys
服务器基础设置
建议学习一下 Linux 常见指令,对服务器操作也有一些了解,掌握 grep 等常见的操作,推荐《⿃哥的 Linux 私房菜》,早日自己玩转 linux。
- 防火墙 & 端口配置:要求不严格可以关闭防火墙,如需开启防火墙请配置打开对应端口号;
- 环境配置:
iterm2 & oh-my-zsh & powerlevel10k; - 根据所需配置告警:内存使用超阈值;带宽不够;结合阿里云监控进行配置;
node 配置
node & npm:下载长期维护版本(注意添加软链设置全局引用)网上资源很多,不一一介绍;nvm / n:node版本维护管理,区别在于nvm是一个完整的shell版本,n是一个npm包,所以n更轻便,但安装路径默认在/usr/local/lib/node_modules下,还是会共用全局的node/npm目录,因此不能很好的满足『按不同node版本使用不同全局node模块』的需求;nrm:设置npm源,如果不用可以将npm默认注册为cnpm;pm2:node进程管理,可以实现性能监控、自动重启、负载均衡等功能;docker:实现node.js应用容器(保证应用间环境隔离,且宿主机能够资源利用最大化);
Nginx
如果项目中只有
node,可以使用node控制path,如果有其他服务器,还是建议使用nginx。
是一款轻量级的 HTTP 服务器,采用事件驱动的异步非阻塞处理方式框架,这让其具有极好的 IO 性能,时常用于服务器的方向代理和负载均衡,建议自学。
主要能力包括:
- 静态资源服务,通过本地文件系统提供服务;
- 反向代理服务,延伸出包括缓存、负载均衡等;
API服务,OpenResty(集成lua的nginx,而lua是c解释执行的脚本语言,保证nginx可以不用c开发,可以有效解决高并发的问题);
小作业:可以尝试使用 goAccess 配置可视化的 nginx 的访问 log。
搭建个人的静态博客
个人建议,可以使用
gatsby,然后部署在自己的服务器上,部署好了可以分享一下,一起看看谁做的博客最吸引人。
- 支持
node配置,插件市场丰富; GraphQL;- 支持数据格式转化,支持以
md格式配置blog; - 支持
TS、SSR、PWA、SEO等;