HTTP协议
HTTP协议
HTTP协议是什么
HTTP:超文本传输协议(Hypertext Transfer Protocol)
为什么需要协议
需要明确的边界
开始
结束
能够携带信息
什么消息
消息类型
...
协议里面有什么(POST为例)
请求包
POST /sis HTTP/1.1
who: Atex
Content-Type: text/plain
Host: 127.0.0.1:8888
Content-Length: 28
Let's watch a movie tofether
应答包
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-Caught: 1650541592984580
OK
请求流程
不足与展望
HTTP框架的设计与实现
分层设计
专注性、扩展性、复用性
高内聚 低耦合、易复用、高扩展性
应用层设计
提供合理的API
可理解性:如ctx.Body(), ctx.GetBody()
不要用 ctx.BodyA()
简单性:如ctx.Request.Header.Peek(key)
/ctx.GetHeader(key)
冗余性、兼容性、可测性、可见性
中间件设计
中间件需求
配合Handler实现一个完整的请求处理生命周期
拥有预处理逻辑与后处理逻辑
可以注册多中间件
对上层模块用户逻辑模块易用
洋葱模型
将核心逻辑与通用逻辑分离, 实现命令一级一级的处理
使用场景
日志记录
性能统计
安全控制
事务处理
异常处理
举例
打印每个请求的request和response
1.既然要实现预处理和后处理,那这个就很像调用了一个函数
2.路由上可以注册多Middleware,同时也可以满足请求级别有效,只需要将Middleware设计为和业务和Handler相同即可
3.用户如果不主动调用下一个处理函数怎么办?
核心:在任何场景下index保证递增
4.出现异常想停止怎么办?
调用链
适用场景
不调用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、/*all
黄金:前缀匹配树
/a/b/c、/a/b/d
如何处理带参数的路由注册?
(处理形如:/a/:id/b类型的路由)
如何匹配HTTP方法?
路由映射表
外层Map:根据method进行初步筛选
如何实现添加多处理函数?
在每个节点上使用一个list存储handler
协议层设计
抽象出合适的接口
1.Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter
2.需要在连接上读写数据
网络层设计
go net "BIO" 用户管理buffer
netpoll NIO 网络库管理buffer
性能修炼之道
针对网络库的优化
go net
存下全部Header
减少系统调用次数
能够复用内存
能够多次读
go net "BIO"
go net with bufio绑定一块缓冲区
netpoll
存下全部Header
拷贝出完整的Body
netpoll with nocopy peek
分配足够大的buffer
限制最大buffer size
不同网络库优势
针对协议的优化 --Headers解析
找到Header Line边界:\r\n
先找到\n再看他前一个是不是\r
那能不能查找更快呢?答:可以运用SIMD
使用和不使用SIMD性能对比
针对协议相关的Headers快速解析:
1.通过Header Key首字母快速筛选除掉完全不可能的key
2.解析对应value到独立字段
3.使用byte slice管理对应header存储,方便复用
热点资源池化
原始方法,就是消息队列来一个处理一个,这样的速度比较慢
池化