HTTTP 设计模型
基础
概念模型
模型和rpc很相似,多了一个路由层
关于协议版本的问题可以看看小林的区别
无视底层
具体设计
啊,我的天,设计一个http框架,
- 首先就要考虑怎么
DispatchServlet:根据路由进行特定方法:路由设计- 连接对象的创建、数据请求响应: read、write:
网络层设计- 拦截器:
中间件设计- 报文解析、压缩:
协议层
应用层设计
提供合理的APi
- 可理解性:如ctx.Body(),ctx.GetBody() 冗余性 不要用ctx.BodyA() 兼容性
- 简单性:如ctx.Request.Header.Peek(key) 可测性 /ctx.GetHeader(key) 可见性
中间件设计
中间件需求:
-
配合Handler实现一个完整的请求处理生命周期
- handler:处理业务
- 中间件:拦截器前后处理逻辑
-
拥有预处理逻辑与后处理逻辑
-
可以注册多中间件
-
对上层模块用户逻辑模块易用
如洋葱模型
拦截器
就像是拦截器一样使用context
- 既然要实现预处理和后处理,那这个就很像调用了一个
拦截器 - 路由上可以注册多Middleware,同时也可以满足请求级别有效,只需要将Middleware设计为和业务和Handler相同即可。 就是调用下一级的函数时使用,
ctx.next()调用下一级:无论是多级拦截还是调用hander都这样写 - 用户如果不主动调用下一个Middleware 怎么办?
核心:在任何场景下index保证递增,自动调用下一个Middleware
-
出现异常想停止怎么办?
调用链
go的协程并不会共享同一个栈,因此有其他问题
适用场景
- 不调用Next:初始化逻辑且不需要在同一调用栈
- 调用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 /a/b/c、/a/b/d/a/:id/c、/*al
- 前缀匹配树 /a/b/c、/a/b/d如何处理带参数的路由注册? (处理形如:/a/:id/b类型的路由)
匹配HTTP方法
路由映射表
外层Map:根据method进行初步筛选
实现添加多处理函数:在每个节点上使用一个Iist存储handler
协议层
type Server interface {
Serve(c context.Context, conn network.Conn) error
}
-
请求参数
不要将Contexts存储在结构类型内部;相反,将Context显式传递给每个需要它的函数。Context应该是第一个参数。
-
连接对象
需要在连接上读写数据
网络层设计
BIO
go func(){
for {
conn,_listener. Accept() //对于每个连接开一个协程
go func (){
conn.Read(request)
handle... //阻塞
conn.Write(response)
}()
}
}()
NIO
go func(){
for {
readableConns,_:=Monitor(conns)//注册监听器,当有一定的数据才唤醒
for conn range readableConns{
conn,_listener. Accept() //对于每个连接开一个协程
go func (){
conn.Read(request)
handle... //阻塞
conn.Write(response)
}()
}
}
}()
接口设计
type Conn interface {
Read(b []byte)(n int,err error)
Write(b []byte)(n int,err error)
}
HTTP优化指标
网路库
就是网络层的设计,如请求响应的Read方法、Write方法、连接对象的处理
优化方向:
- 存下全部Header
- 减少系统调用次数
- 能够复用内存
- 能够多次读
1、go net
go的net默认使用网路库模型BIO,每次buf不够才会扩容
2、go net with bufio:绑定一块缓冲区
3、netpoll
- 存下全部Header
- 拷贝出完整的Body
4、netpoll with nocopy peek
- 分配足够大的buffer
- 限制最大buffer size
网络库
- C10K Problem
- Select,Poll,Epoll
- Epoll ET、LT 区别
- 字节跳动自研网络库 netpoll,netpoll-examples
针对协议的优化
Headers解析
Header key规范化
aaa-bbb->Aaa-Bbb
热点资源池化
找到Header Line边界:r\n 先找到n再看它前一个是不是\r
func index (b []byte,c byte) int {
for i :=0;i<len(b);i++{
if b[i]==c {
return i
}
return -1
}
那能不能更快呢?SIMD
针对协议相关的Headers快速解析:
1.通过Header key首字母快速筛除掉完全不可能的key 2.解析对应value到独立字段 3.使用byte slice管理对应header存储,方便复用
请求体中同样处理的Key:User-Agent、Content-Type、Content-Length、Connection、Transfer-Encoding