这是我参与「第五届青训营 」笔记创作活动的第17天
只不过是字节给我的任务罢了
HTTP
Hyper Text Transfer Protocol
- 协议开始
- 协议元数据
- Text
- 协议结束
POST请求
POST/sis HTTP/1.1
who:Alex
Content-Type: text/plain
Host: 127.0.0.1:8888
Content-length: 28
Let's watch a movie together
响应
HTTP/1.1 200 OK
Sever: hertz
Data: Thu, 21 Apr 2022 11:46:32 GMT
Content-Type: text/plain; charset=utf-8
Content-length: 2
Upstream-Caught: 1650541592984580
OK
协议的组成
- 请求行/状态行
- 方法名
- GET HAED POST PUT DELETE CONNECT OPTIONS TRACE PATCH
- URL
- 协议版本
- 状态码
- 1xx,信息类
- 2xx,成功
- 3xx,重定向
- 4xx,客户端错误
- 5xx,服务端错误
- 状态码描述
- 方法名
- 请求头/响应头
- 协议相关
- 业务相关
- 请求体/响应体
请求流程
路由层根据url选择执行的handler
不同版本HTTP对比
- HTTP1
- 队头阻塞
- 传输效率低
- 明文传输不安全
- HTTP2
- 多路复用
- 头部压缩
- 二进制协议
- QUIC
- 基于UDP实现
- 解决队头阻塞
- 加密减少握手次数
- 支持快速启动
分层设计
-
高内聚,低耦合
-
易复用
-
高扩展性
上图的Common层为通用层,其他层都会使用
一个切实可行的复杂系统应该由一个切实可行的简单系统发展而来,从头设计复杂系统是不切实可行的,无法修修补补让它切实可行,必须从切实可行的简单系统开始。
应用层设计
提供合理的API
-
可理解性:如ctx.Body(),ctx.GetBody()
-
简单性:
ctx.Request.Header.Peek(key)--> ctx.GetHeader(key) -
冗余性
-
兼容性
-
可测性
-
可见性
中间件设计
-
配合Handler实现一个完整的请求处理生命周期
-
拥有预处理逻辑和后处理逻辑
-
可以注册多中间件
-
对上层模块用户逻辑模块易用
-
洋葱模型
-
实现预处理和后处理,可以将中间件和业务逻辑代码合并为Next()函数
路由设计
框架路由实际上是为了URL匹配对应的处理函数(Handlers)
-
静态路由:/a/b/c、a/b/d
-
参数路由:/a/:id/c(/a/b/c,/a/d/c)、/*all
-
路由修复:/a/b<------>/a/b/
-
冲突路由以及优先级:/a/b、/:id/c
-
匹配HTTP方法
-
多处理函数,方便添加中间件
-
青铜:map[string]handlers
-
黄金:前缀匹配树
-
匹配HTTP方法
路由映射表,外层Map:根据method进行初步筛选
-
实现添加多处理函数:使用list存储handler
node struct{ prefix string parent *node children children handlers app.HandlerChain }
如何做设计
- 明确需求:考虑清楚需要解决什么问题,有哪些需求
- 业界调研:业界都有哪些解决方案可供参考
- 方案权衡:思考不同方案的取舍
- 方案评审:相关同学对不同方案进行评审
- 确定开发:确定最合适的方案进行开发
协议层设计
-
抽象出合适的接口
type Server interface{ Serve(c context.Context, conn network.conn)`error }
不要把上下文参数封装到结构体中,应该把上下文作为函数参数传递
需要在连接上读写数据
网络层设计
BIO:阻塞IO
go net
type Conn interface{
Read(b []byte)(n int, err error)
Write(b []byte)(n int, err error)
}
NIO:非阻塞IO
type Reader interface{
Peek(n int)([]byte, error)
}
type Writer interface{
Malloc(n int)(buf []byte, err error)
Flush() error
}
性能优化
对于go net
- 存下全部Header
- 减少系统调用次数
- 能够复用内存
- 能够多次读
绑定一块缓冲区,使用完后对其进行回收,保证下一次请求可以复用这块空间
对于net poll
为了减少锁竞争,使用链表实现无锁化
-
存下全部Header
-
拷贝出完整的Body
分配足够大的buffer,限制最大buffer size
- go net
- 流式友好
- 小包性能好
- net poll
- 中大包性能高
- 时延低
协议优化
找到Header Line的边界\r\n,两个连续的\r\n说明Header读完了