HTTP协议
图片都被过滤掉了,不想一个一个粘贴了。
带图片的在我博客
HTTP:超文本传输协议(Hypertext Transfer Protocol)
为什么需要协议?
- 传输内容需要明确的边界(开始、结束)
- 能够携带信息
- 什么消息
- 消息类型
协议内容
常见方法名
- GET
- HEAD
- POST
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
- PATCH
状态码
- 1xx:信息类
- 2xx:成功
- 3xx:重定向
- 4xx:客户端错误
- 5xx:服务端错误
请求
GET
GET /api/photos/albums HTTP/1.1 //请求方式,路径,版本
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,zh-CN;q=0.9
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0OTQ0ZjUzMmUwNGQ0ZjA3YjA1MTJjYmRjMTE0ZDA1OSIsInN1YiI6IjEiLCJpc3MiOiJodWF3ZWltaWFuIn0.w968iHauYbqGp6pbqErVycdQgxL1AAFaVhCjYr-flds
Cache-Control: no-cache
Connection: keep-alive
Cookie: __qc_wId=183; theme=theme-dark; locale=cn
Host: www.lazycat.asia
Pragma: no-cache
Referer: https://www.lazycat.asia/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36
sec-ch-ua: "Google Chrome";v="113", "Chromium";v="113", "Not-A.Brand";v="24"
sec-ch-ua-mobile: ?1
POST
POST /api/report HTTP/1.1
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,zh-CN;q=0.9
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0OTQ0ZjUzMmUwNGQ0ZjA3YjA1MTJjYmRjMTE0ZDA1OSIsInN1YiI6IjEiLCJpc3MiOiJodWF3ZWltaWFuIn0.w968iHauYbqGp6pbqErVycdQgxL1AAFaVhCjYr-flds
Cache-Control: no-cache
Connection: keep-alive //长连接
Content-Length: 0 //传输文本长度
Cookie: __qc_wId=183; theme=theme-dark; locale=cn
Host: www.lazycat.asia
Origin: https://www.lazycat.asia
Pragma: no-cache
Referer: https://www.lazycat.asia/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36
sec-ch-ua: "Google Chrome";v="113", "Chromium";v="113", "Not-A.Brand";v="24"
sec-ch-ua-mobile: ?1
sec-ch-ua-platform: "Android"
响应
GET
HTTP/1.1 200
Server: nginx/1.23.4
Date: Tue, 23 May 2023 02:11:12 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
POST
HTTP/1.1 200
Server: nginx/1.23.4
Date: Tue, 23 May 2023 02:11:12 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: https://www.lazycat.asia
Access-Control-Allow-Credentials: true
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
协议内容大体相同,就举GET和POST大致对比一下它们的区别
请求流程
不足与展望
上述,以及目前主流的HTTP协议采用是HTTP/1.1版本。
HTTP1的缺点
- 队头阻塞
- 传输效率低
- 明文传输不安全
针对HTTP1的缺陷,出现了效率更高的HTTP2和QUIC协议
HTTP2
- 多路复用
- 头部压缩
- 二进制协议
QUIC
- 基于UDP实现
- 解决对头阻塞
- 加密减少握手次数
- 支持快速启动
HTTP框架的设计与实现
网络的分层设计

分层设计思想:高内聚、低耦合、易复用、高扩展性
一个切实可行的复杂系统势必是从一个切实可行的简单系统发展而来的。从头开始设计的复杂系统根本不切实可行,无法修修补补让它切实可行。你必须由一个切实可行的简单系统重新开始
———盖尔定律
应用层设计-Application
提供合理的API
- 可理解性:如
ctx.Body(),ctx.GetBody()不要用ctx.BodyA() - 简单性:如
ctx.Request.Header.Peek(key),ctx.GetHeader(key) - 冗余性
- 兼容性
- 可测性
- 可见性
不要试图搞一个奇怪的名称API然后写在API文档中说明,因为有些傻子压根不看文档
中间件设计-middleware
中间件需求
- 配合Handler实现一个完整的请求处理生命周期
- 拥有预处理逻辑与后处理逻辑
- 可以注册多中间件
- 对上层模块用户逻辑模块易用
洋葱模型
核心逻辑与通用逻辑分离 ----> 有点像spring的AOP
适用场景
- 日志记录
- 性能统计
- 安全控制
- 事务处理
- 异常处理
举例
打印每个请求的request和response
插入日志未使用中间件
插入日志使用中间件
中间件调用链设计
路由设计-route
框架路由实际上就是未URL匹配对应的处理函数(Handlers)
- 静态路由:a/b/c
- 参数路由:/a/:id/c、/*all
- 路由修复:/a/b <--->/a/b/
- 冲突路由以及优先级:/a/b、/:id/c
- 匹配HTTP方法
- 多处理函数:方便添加中间件
如何匹配HTTP方法?
路由映射表
外层Map:根据method进行初步筛选
如何实现添加多处理函数?
在每个节点上使用一个list存储handler
协议层设计-codec
- 抽象出合适的接口
网络层设计-transport
//go net "BIO" "用户管理buffer"
type Conn interface{
Read(b []byte)(n int,err error)
Write(b []byte)(n int,err error)
}
//netpoll "NIO" "网络库管理buffer"
type Reader interface{
Peek(n int)([]byte,error)
}
type Writer interface{
Malloc(n int)(buf []byte,err error)
Flush() error
}
type Conn interface{
net.Conn
Reader
Writer
}
netpoll地址:github.com/cloudwego/n…
总结
API设计:可解性、简单性...
中间件设计:洋葱模型
路由设计:前缀匹配树
协议层设计:抽象出合适的接口
网络层设计:网络模型
性能优化
针对网络库的优化
go net "BIO"
- 存下全部Header
- 减少系统调用次数
- 能够复用内存
- 能够多次读
go net with bufio绑定一块缓冲区
大部分包都是在4k以下的,所以我们可以绑定一块大小为4k左右的一个缓冲区,这样对内存的压力也不是很大。
netpoll
- 存下全部Header
- 拷贝出完整的Body
netpoll with nocopy peek
- 分配足够大的buffer
- 限制最大buffer size
不同网络库优势
go net
- 流式友好
- 小包性能
netpoll
- 中大包性能高
- 时延低
针对协议的优化
Headers解析
针对协议相关的Headers快速解析:
- 通过Header key首字母快速筛除掉完全没有可能的key
- 解析对应value到独立字段
- 使用byte slice管理对应header存储,方便复用
请求体中相同处理的Key:
User-Agent、Content-Type、Content-Length、Connection、Transfer-Encoding
优势:
- 核心字段快速解析
- 使用byte slice存储
- 额外存储到成员变量中
缺点:
- 普通header性能较低
- 没有map结构
热点资源池化
池化前
池化后
优势:
- 减少了内存分配
- 提高了内存复用
- 降低了GC压力
- 性能提升
缺点:
- 额外的Reset逻辑
- 请求内有效
- 问题定位难度增加
总结
针对网络库的优化:buffer设计
正对协议的优化:header解析、热点资源池化
Hertz
看官网吧
上述文档中的图片中的代码很多都是Hertz的框架代码
Hertz官网