OSI 七层模型 和 TCP/IP 四层模型
DNS 域名解析
根域名 - 顶级域名 - 权威域名
浏览器缓存 - 操作系统缓存 - host 文件缓存 - DNS服务器缓存 - DNS 三级查找
URI: 协议+域名+路径
UDP. SPDY.
常用头字段
HTTP 协议规定了非常多的头部字段,实现各种各样的功能,但基本上可以分为四大类:
- 通用字段:在请求头和响应头里都可以出现;
- 请求字段:仅能出现在请求头里,进一步说明请求信息或者额外的附加条件;
- 响应字段:仅能出现在响应头里,补充说明响应报文的信息;
- 实体字段:它实际上属于通用字段,但专门描述 body 的额外信息。
常见的 HTTP 请求头
Accept:
Accept-Language:
Accept-Encoding:
Referer:
Host:
Cache-Control:
User-Agent”
常见的 HTTP 响应头
http 的特点
- 无状态
- 灵活可扩展
- 请求应答的方式
- 基于tcp协议是可靠的
- 应用层协议,可以支持很多合适的传输
http的优缺点
- 易扩展
- 环境生态成熟
- 明文传输 方便分析 但容易被拦截
- 无状态 方便负载均衡 但每次需要单独的校验身份
- 请求应答 可靠 但性能有影响 不够快
大文件传输
1.分块传输
请求头字段:Transfer-Encoding: chunked
2.知道文件总大小 Content-length:
范围请求 响应状态码必须是 206
客户端请求:Range: bytes=0-99( 请求头 Range 是 HTTP 范围请求的专用字段,格式是“bytes=x-y” )
服务器: 要添加一个响应头字段Content-Range,告诉片段的实际偏移量和资源的总大小,格式是“bytes x-y/length”
服务端返回:Accept-Ranges: bytes
断点续传要点:
不仅看视频的拖拽进度需要范围请求,常用的下载工具里的多段下载、断点续传也是基于它实现的,要点是:
- 先发个 HEAD,看服务器是否支持范围请求,同时获取文件的大小;
- 开 N 个线程,每个线程使用 Range 字段划分出各自负责下载的片段,发请求传输数据;
- 下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了。
Nginx配置
gzip on
- 使用“keepalive_timeout”指令,设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源。
- 使用“keepalive_requests”指令,设置长连接上可发送的最大请求次数。比如设置成 1000,那么当 Nginx 在这个连接上处理了 1000 个请求后,也会主动断开连接。
队头阻塞”问题会导致性能下降,可以用“并发连接”和“域名分片”技术缓解。
利用HTTP 的长连接特性对服务器发起大量请求,导致服务器最终资源拒绝服务 这就是常说的DDoS攻击
Connection 字段还有一个取值:”Connection: Upgrade”,配和状态码101 表示协议升级,例如从HTTP 切换到 WebSocket
重定向
301
302
Location 字段
Cookie
为了保护 Cookie,还要给它设置有效期、作用域等属性,常用的有 Max-Age、Expires、Domain、Path HttpOnly 等
浏览器缓存
协商缓存
Cache-control: max-age=200 expire=时间
条件缓存
If-Modify-since: 时间
last-modify: 时间
ETG: ‘25456451’
If-None-Match: ‘25456451’
304 命中缓存
代理服务器
Via 代替自己的身份
- 专门的“代理协议”可以在不改动原始报文的情况下传递客户端的真实 IP。
- 如果想要知道客户端的真实 IP 地址,可以使用字段“X-Forwarded-For”和“X-Real-IP”;
反向代理中使用的负载均衡算法
- 轮询是upstream的默认分配方式
- 轮询的加强版 即可指定轮询比率 weight和访问几率成正比
- .ip_hash 每个请求按照访问ip (即Nginx的前置服务器或客户端IP)的hash结果分配,这样每个访客会固定访问一个后端服务器,可以解决session(会话)一致问题
- fair 公平的按照后端服务器的响应时间(rt) 来分配请求 ,响应时间短即rt小的后端服务器优先分配请求
- url_hash 与ip_hash类似,但是按照访问url的hash结果来分配请求,使得每个url定向到同一个后端服务,主要用于后端服务器为缓存的场景下
代理缓存
4 种服务器端的“Cache-Control”属性:max-age、no_store、no_cache 和 must-revalidate
HTTPS
只有同时具备了
机密性、完整性、身份认证、不可否认这四个特性,
通信双方的利益才能有保障,才能算得上是真正的安全
SSL/TLS
密码套件,协商选定的是“ECDHE-RSA-AES256-GCM-SHA384”
TLS 的密码套件命名非常规范,格式很固定。基本的形式是“密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法”
握手时使用 ECDHE 算法进行密钥交换,用 RSA 签名和身份认证,握手后的通信使用 AES 对称算法,密钥长度 256 位,分组模式是 GCM,摘要算法 SHA384 用于消息认证和产生随机数
机密性由对称加密AES保证,完整性由SHA384摘要算法保证,身份认证和不可否认由RSA非对称加密保证
可以得到 TLS 密码套件中定义的对称加密算法
比如,AES128-GCM,意思是密钥长度为 128 位的 AES 算法,使用的分组模式是 GCM;
非对称加密算法的设计要比对称算法难得多,在 TLS 里只有很少的几种,比如 DH、DSA、RSA、ECC 等。
RSA 可能是其中最著名的一个,几乎可以说是非对称加密的代名词
这就是现在 TLS 里使用的混合加密方式,其实说穿了也很简单:
在通信刚开始的时候使用非对称算法,比如 RSA、ECDHE,首先解决密钥交换的问题。
然后用随机数产生对称算法使用的“会话密钥”(session key),再用公钥加密。因为会话密钥很短,通常只有 16 字节或 32 字节,所以慢一点也无所谓。
对方拿到密文后用私钥解密,取出会话密钥。这样,双方就实现了对称密钥的安全交换,后续就不再使用非对称加密,全都使用对称加密。
TLS 1.2 密钥交换算法ECDHE
TLS 1.2 密钥交换算法RSA
TLS 1.3密钥交换算法ECDHE
所有加密算法:
非对称加密:ECDHE、
对称加密:AES、ChaCha20、
散列加密:SHA-2
- TLS1.3 也简化了握手过程,完全握手只需要一个消息往返,提升了性能。
HTTPS 的优化
响应头加 HSTS指令 (只能用HTTPS)
add_header Strict-Transport-Security max-age=15768000;
CSP(Content Security Policy)的各种指令和标签来配置安全策略
HTTP2
HTTP2 实际上是建立在 TCP 和 TLS 协议之上的,所以会分别进行一次 TCP 握手和 TLS 握手
TLS 握手之后客户端必须发送一个请求前言 (connection preface), 用来确认建立 HTTPS/2 连接
这个“连接前言”是标准的 HTTP/1 请求报文,使用纯文本的 ASCII 码格式,请求方法是特别注册的一个关键字
“PRI”,全文只有 24 个字节
HTTP/2 请求
伪头字段
为了与“真头字段”区分开来,这些“伪头字段”会在名字前加一个“:”,比如“:authority” “:method” “:status”,分别表示的是域名、请求方法和状态码。
http 2 帧
性能优化
头部压缩:
HPACK 算法 在客户端和服务端建立字典,通过索引表示重复的字符串,还釆用哈夫曼编码来压缩整数和字符串,可以达到 50%~90% 的高压缩率
二进制格式
它把 TCP 协议的部分特性挪到了应用层,把原来的“Header+Body”的消息“打散”为数个小片的二进制“帧”(Frame),用“HEADERS”帧存放头数据、“DATA”帧存放实体数据。
Stream 流式传输
解决队头阻塞,多路服用
HTTP/3
QUIC 就选定了 UDP,在它之上把 TCP 的那一套连接管理、拥塞窗口、流量控制等“搬”了过来,“去其糟粕,取其精华”,打造出了一个全新的可靠传输协议,可以认为是“新时代的 TCP”。
Nginx 服务器
Nginx 的 HTTP 处理有四大类模块:
- handler 模块:直接处理 HTTP 请求;
- filter 模块:不直接处理请求,而是加工过滤响应报文;
- upstream 模块:实现反向代理功能,转发请求到其他服务器;
balance 模块:实现反向代理时的负载均衡算法。
Nginx 是一个高性能的 Web 服务器,它非常的轻量级,消耗的 CPU、内存很少;
- Nginx 采用“master/workers”进程池架构,不使用多线程,消除了进程、线程切换的成本;
- Nginx 基于 epoll 实现了“I/O 多路复用”,不会阻塞,所以性能很高;
- Nginx 使用了“职责链”模式,多个模块分工合作,自由组合,以流水线的方式处理 HTTP 请求。
Websocket
WebSocket 的握手是一个标准的 HTTP GET 请求,但要带上两个协议升级的专用头字段:
- “Connection: Upgrade”,表示要求协议“升级”;
- “Upgrade: websocket”,表示要“升级”成 WebSocket 协议。
- Sec-WebSocket-Key:一个 Base64 编码的 16 字节随机数,作为简单的认证密钥;
- Sec-WebSocket-Version:协议的版本号,当前必须是 13。
看到上面的四个字段,就知道这不是一个普通的 GET 请求,而是 WebSocket 的升级请求,于是就不走普通的 HTTP 处理流程,而是构造一个特殊的“101 Switching Protocols”响应报文,通知客户端,接下来就不用 HTTP 了,全改用 WebSocket 协议通信。
WebSocket 的握手响应报文也是有特殊格式的,要用字段“Sec-WebSocket-Accept”验证客户端请求报文,同样也是为了防止误连接。
具体的做法是把请求头里“Sec-WebSocket-Key”的值,加上一个专用的 UUID “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,再计算 SHA-1 摘要。
客户端收到响应报文,就可以用同样的算法,比对值是否相等,如果相等,就说明返回的报文确实是刚才握手时连接的服务器,认证成功。
握手完成,后续传输的数据就不再是 HTTP 报文,而是 WebSocket 格式的二进制帧了