1 HTTP简介
HTTP(Hypertext Transfer Protocol):超文本传输协议
1.1 为什么需要协议
协议的要素:
- 明确的边界
- 能够携带的信息及信息类型
1.2 协议内容
(可以参考谢希然的《计算机网络》教材)
- 请求行/状态行:方法名、URL、协议版本//协议版本、状态码、状态码描述
- 请求头/响应头
- 请求体/响应体
1.3 请求流程
服务治理层是依托中间件层的。
1.4 不足与展望
1.4.1 HTTP1
基于TCP。
缺陷:
- 队头阻塞
- 传输效率低
- 明文传输不安全
1.4.2 HTTP2
解决的问题:
- 多路复用
- 头部压缩
- 二进制协议
仍然基于TCP,因此HTTP1中的部分问题依然没有解决
1.4.3 QUIC
基于UDP。 解决的问题:
- 解决了队头阻塞
- 加密来减少握手次数
- 支持快速启动
2 HTTP框架的设计与实现
2.1 分层设计
分层设计的特性:
- 高内聚
- 低耦合
- 易复用
- 高扩展性
2.2 应用层设计
以下是一些需要注意的点:
- 提供合理的API,注意命名规范
- 保证可理解性
- 保证简单性
- 保证冗余性
- 保证兼容性
- 保证可测性
- 保证可见性
2.3 中间件设计
中间件的需求:
- 配合Handler实现一个完整的请求处理生命周期
- 拥有预处理逻辑和后处理逻辑
- 可以注册多中间件
- 对上层模块用户逻辑模块易用
大多数中间件模型都采用洋葱模型。
2.4 路由设计
框架路由实际上就是为 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语句设计。- 优点:速度快
- 缺点:仅限于静态路由
- 中级:设计前缀匹配树
路由映射表: Map -> Method (String) -> (前缀树+头节点(*node))
匹配HTTP方法的方式: 外层Map:根据 method 进行初步筛选
添加多处理函数的方法:在每个节点上使用一个 list 存储 handler
2.5 协议层设计
重点:需要抽象出一个合适的接口
2.6 网络层设计
2.6.1 BIO(阻塞IO)
例:
go func(){
for {
conn,_ :=listener.Accept()
go func (){
conn.Read(request)
handle...
conn.Write(response)
}()
}
}()
Go官方采用BIO方式。
2.6.2 NIO
例:
go func(){
for{
readableConns, _ := Monitor(conns)//注册一个监听器,读取到足够的数据后再进行唤醒
for conn := range readableConns{
go func(){
conn.Read(request)
handle...
conn.Write(response)
}
}
}
}
netpoll工具采用了NIO的方式。
3 框架优化
3.1 针对网络库的优化
对于go net的需求:
- 存下全部的 Header
- 减少系统调用次数
- 能复用内存
- 能多次读
解决办法:采用
go net with bufio,为每个net绑定一个缓冲区。
对于netpoll的需求:
- 存下全部的Header
- 拷贝出完整的Body
解决办法:采用
netpoll with nocopy peek,分配足够大的buffer的同时限制最大buffer size
以上二者的优势区别:
- go net:
- 流式友好
- 小包性能高
- netpoll
- 中大包性能高
- 时延低
3.2 针对协议的优化
主要是针对 Headers 的解析方面进行优化。
需要找到连续的Header Line边界\r\n才能判断其完整性。优先找\n再看前面一个字符是不是\r。
SIMD:单指令多数据流(Single Instruction, Multiple Data),是一种并行计算技术。它允许一条指令同时处理多个数据元素,从而提高计算机系统的性能。SIMD体系结构在处理需要大量数据并且可以同时进行相同操作的任务时特别有效,例如图像处理、视频编解码、信号处理等。核心思想是在一条指令中并行执行相同的操作,但是操作的数据可以是多个数据元素。这种并行执行可以在一个时钟周期内完成多个数据的处理,从而提高了计算速度。在SIMD体系结构中,通常有一个向量处理器或SIMD单元,它包含多个处理单元或者计算单元,每个处理单元可以同时对一个数据元素执行相同的操作。这些数据元素通常被组织成向量或者矩阵形式,被一条指令同时操作。
应用SIMD可以更快速地找到边界。
解析办法:
- 通过 Headers Key 首字母快速筛除完全不可能的 Key
- 解析对应 value 到独立字段
- 使用 byte slice 管理对应 header 存储,方便复用
3.3 热点资源池化
可以对Request Context进行复用,从而减少内存和GC的压力,提高性能。但是也会增加问题定位的难度。