HTTP框架修炼之道 | 豆包MarsCode AI刷题
课程目录
- 走进HTTP协议
- HTTP框架的设计与实现
- 性能修炼之道与企业实践
走进HTTP协议
目录:
- 再谈HTTP协议
- HTTP框架的设计与实现
- 性能修炼之道
- 企业实践
再谈HTTP协议
HTTP协议是什么
HTTP:超文本传输协议Hypertext Transfer Protocil
为什么需要协议?
线上都是01数据流,需要协议才能互相理解
数据传输,需要明确的边界:协议开始-协议元数据-Text-协议结束
能够携带消息:什么消息,消息的类型是什么
一个常见的POST请求做了什么?
第一行(请求行):协议方法,URL,协议版本
元数据:发信人,文本类型,主机,文本长度
文本本体
回应:
第一行:协议版本,状态码
服务器
时间戳
文本格式,解码方式
文本长度
数据流编号
文本本体
协议里有什么
-
请求行/状态行
请求行:
-
方法名
- GET,HEAD,POST,PUT,DELETE,CONNECT,OPTIONS,TRACE,PATCH
-
URL
-
协议版本
状态行:
-
协议版本
-
状态码
- 1xx:信息类
- 2xx:成功
- 3xx:重定向
- 4xx:客户端错误
- 5xx:服务端错误
-
状态码描述
-
-
请求头/响应头
- 协议相关:比如表明文本长度
- 业务相关:自行设定的元数据
-
请求体/响应体
请求流程
业务层
服务治理层/中间件层
路由层
协议编(解)码层
传输层
不足与展望
-
HTTP1:
- 队头阻塞
- 传输效率低
- 明文传输不安全
-
HTTP2
- 多路复用
- 头部压缩
- 二进制协议
-
QUIC
- 基于UDP实现
- 解决队头阻塞
- 加密减少握手次数
- 支持快速启动
HTTP框架的设计与实现
分层设计
专注性,扩展性,复用性
| OSI七层网络模型 | TCP/IP四层概念模型 | 对应网络协议 |
|---|---|---|
| 应用层 | 应用层 | HTTP、TFTP、FTP、NFS、WAIS、SMTP |
| 表示层 | 应用层 | Telnet、Rlogin、SNMP、Gopher |
| 会话层 | 应用层 | SMTP、DNS |
| 传输层 | 传输层 | TCP、UDP |
| 网络层 | 网络层 | IP、ICMP、ARP、AKP、UUCP |
| 数据链路层 | 数据链路层 | FDDI、Ethernet、Arpanet、PDN、SLIP、PPP |
| 物理层 | 数据链路层 | IEEE |
高内聚,低耦合,易复用,高扩展性
一个切实可行的复杂系统势必是从一个切实可行的简单系统发展而来的,从头开始设计的复杂系统根本不切实可行,无法修修补补让他运行。
应用层设计
提供合理的API
- 可理解性:
如ctx.Body(),ctx.GetBody() - 简单性:如
ctx.Request.Header.Peek(key)可以直接用/ctx.GetHeader(key)概括 - 冗余性
- 兼容性
- 可测性
- 可见性
不要试图在文档中说明,很多用户不看文档
中间件设计
中间件需求:
- 配合Handler实现一个完整地请求处理生命周期
- 拥有与处理逻辑与后处理逻辑
- 可以注册多中间件
- 对上层模块用户逻辑易用
经典中间件模型-洋葱模型
路由设计
框架路由实际上就是为URL匹配对应的处理函数
- 静态路由
- 参数路由
- 路由修复
- 冲突路由以及优先级
- 匹配HTTP方法
- 多处理函数:方便添加中间件
前缀匹配树
如何匹配HTTP方法——路由映射表
- 明确需求:考虑清楚要解决什么问题,有哪些需求
- 业界调研:业界都有哪些解决方案可供参考
- 方案权衡:思考不同方案的取舍
- 方案评审:相关同学对不同方案做评审
- 确定开发:确定最合适的方案进行开发
协议层设计
抽象出合适的接口
网络层设计
netpoll:https://github.com/cloudwego/netpoll
针对网络模型进行设计
性能修炼之道
针对网络库的优化
针对协议的优化
针对网络库的优化
get net:我们希望有一个地方:
- 存下所有的Header
- 减少系统调用次数
- 能够复用内存
- 能够多次读
于是选择绑定一块缓冲区,大小大约4kb get net with bufio
netpoll:
- 存下所有的Header
- 拷贝出完整的Body
- 分配足够大的buffer
- 限制最大buffer size
netpoll with nocopy peek
针对协议的优化-Header解析
找到Header Line边界 \r\n
SIMD加速运行
针对协议相关的Headers快速解析:
- 通过Header key首字母快速筛除掉完全不可能的key
- 解析对应value到独立字段
- 使用byte slice管理对应headert存储,方便复用
请求体中同样处理的Key: User-Agent,Content-Type、Content-Length、Connection、Transfer-Encoding
-
取:
- 核心字段快速解析
- 只用byte slice存储
- 额外存储到成员变量中
-
舍:
- 普通header性能较低
- 没有map结构
Header key规范化:
直接将字节全部转换为ASCII码,采用加法减法,直接查找Header核心字符
-
取:
- 超高的转换效率
- 比net.http提高40倍
-
舍:
- 额外的内存开销
- 变更困难
针对协议的优化-热点资源池化
一个请求进来之后,会有一个RequestContext与请求一一对应,贯穿一个请求始终
在高并发场景,如果我们对每一个请求进行分配或者释放,对内存也有较大的压力。这种场景,我们可以有一个RequestContext的池子。
请求来的时候,从池子取出;请求处理完毕,放回池子,实现复用
-
取:
- 减少内存分配
- 提高内存复用
- 降低GC压力
- 性能提升
-
舍:
- 额外的Reset逻辑
- 请求内有效
- 问题定位难度增加
企业实践
追求性能:能够支持更多的并发,一个框架的优劣从性能判断
追求易用,减少无用
打通内部生态:社区环境,更新迭代的活力。通用逻辑整合进框架,降低上手难度
文档建设,用户群建设:常见问题沉淀下来,整理成文档
内部HTTP框架:Hertz