class 4-1
http框架修炼之道
前端和后端通过http请求交流
http:Hypertext Transfer Protocol 超文本传输协议
明确的边界:协议开始 -- text --协议结束
携带信息的类型: 协议开始 -- 协议元数据 -- text --协议结束
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 //文本/请求体
//换行符,标志协议结束
Server回复/响应协议:
HTTP/1.1 200 OK //状态行
Server: hertz
Date: Thu, 21 Apr 2022 11:46:32 GMT
Content-Type: text/plain; charset=utf-8
Content-length: 2
Upstream: 1650541592984588 //响应头
OK //响应体
请求行:方法名(常用方法名:GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH)、URL、协议版本
状态行:协议版本、状态码、状态码描述
状态码:1xx:信息类,2xx:成功,3xx:重定向,4xx:客户端错误,5xx:服务端错误
HTTP不足与展望
- HTTP1:基于TCP,队头阻塞,传输效率低,明文传输不安全
- HTTP2:基于TCP,多路复用,头部压缩,二进制协议
- QUIC:基于UDP实现,解决队头阻塞,加密减少握手次数,支持快速启动
class4-2:HTTP框架的设计与实现
分层设计:专注性、扩展性、复用性。专注于特定层的设计,直接使用下层提供的接口
传输层:TCP,UDP
应用层:HTTP
TCP/IP四层概念模型:应用层,传输层,网络层,数据链路层
应用层(抽象请求) -- 中间件层(预处理/后处理逻辑) -- 路由层(注册/寻址) -- 协议层(通过接口,完成协议扩展) -- 网络层(灵活替换网络库)
应用层设计
提供合理的API: 可理解性、简单性、冗余性、兼容性、可测性、可见性
中间件设计
中间件需求:
- 配合Handler实现一个完整的请求处理生命周期
- 拥有预处理逻辑与后处理逻辑
- 可以注册多中间件
- 对上层模块、用户逻辑模块易用
经典中间件模型:洋葱模型(将核心逻辑与通用逻辑分离)
Request -- 日志(预处理) -- Metrics(预处理) -- Biz Handler(执行业务逻辑) -- Metrics(后处理) -- 日志(后处理) -- Response
路由设计
框架路由实际上就是为URL匹配对应的处理函数
- 静态路由:/a/b/c
- 参数路由:/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(string) -- 前缀树 -- 头节点(*node)
外层Map:通过Method初步筛选HTTP方法
实现添加多处理函数:在每个节点上使用list存储handler
如何设计路由
- 明确需求
- 业界调研
- 方案权衡
- 方案评审
- 确定开发
协议层设计
抽象出合适的接口;
type Swever interface{
Serve(c context.Context, conn network.Conn) error
}
传输层:网络层设计
BIO:blockIO,用户管理buffer,go.net
go func() {
for {
conn, - := listener.Accept()
go func() {
conn.Read(request)
handle...
conn.Write(response)
}()
}
}()
NIO:注册监听器,监听到足够数据后唤醒,解决堵塞,网络库管理buffer,go.netpoll
go func() {
for {
readableConns, _ := Monitor(conns)
for conn:= range readableConns {
go func() {
conn.Read(request)
handle...
conn.Write(response)
}()
}
}()
class4-3: 性能修炼之道
针对网络库的优化:buffer设计
-
go net:流式友好(需要用户手动调用),小包性能高 需求:存下全部Header,减少系统调用次数,能够复用内存,能够多次读
-
go net with bufio:绑定一块缓冲区(一般是4k,若超过,则需申请额外的buffer,并需完成buffer的回收)
-
netpoll:存下全部Header,拷贝出完整的Body:中大包性能高,时延低
-
netpoll with nocopy peek:分配足够大的buffer,限制最大 buffer size
针对协议的优化 - Headers解析:headers没有长度,需要进行解析
找到Header Line边界:\r\n 先找到\n,再看前面是不是\r
SIMD: 多数据流技术,可以用一组指令对多组数据进行变层操作
针对协议相关的Headers快速解析:
- 通过Header key首字母快速筛除掉完全不可能的KEY
- 解析对应value到独立字段
- 使用byte slice管理对应header存储,方便使用
Header key 规范化 aaa-bbb --> Aaa-Bbb
热点资源池化
RequestContext:池,应用于高并发场景,Request来,从池子拿出Request Context,Response之后,再放回池子,下一次请求可以复用,减少Runtime压力
优势:减少内存分配,提高内存复用,降低GC压力,性能提升
劣势:额外的Reset逻辑,请求内有效(生命周期),问题定位难度增加
企业实践:框架设计
- 追求性能
- 追求易用,减少误用
- 打通内部/外部生态
- 文档建设,用户群建设
内部HTTP框架:Hertz