前后端之间通过HTTP通信
HTTP框架负责对HTTP请求的解析
根据对应的路由选择对应的逻辑
1. HTTP协议
1.1 HTTP是什么
1991年开始大规模使用
HTTP:超文本传输协议(Hypertext Transfer Protocol)
起初人和计算机对话:通过text文本交互
后来通网了,计算机与计算机之间也交流
后来,又有了图片、链接、视频,这就是超文本
为什么需要协议
正如说话有主谓宾这些语法一样,传输的数据也要有一定的规则才能让对方读懂。
协议规定了明确边界
我们需要知道信息是什么时候开始和结束
能够携带什么信息
需要语言信息描述它是什么类型的信息
把这些语言信息放到对应的地方进行传输
1.2. 协议里有什么
以POST请求为例
场景:和对方交流,请对方看电影
请求行/状态行
请求行:
方法名 URL 协议版本
方法名:GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATCH
状态行:
协议版本 状态码 状态码描述
状态码:
1xx:信息类
2xx:成功
3xx:重定向
4xx:客户端错误
5xx:服务端错误
请求头/响应头
请求体/响应体
1.3 请求流程
业务层
业务方使用框架提供api完成业务逻辑
服务治理层 依托于中间件层
中间件层和请求级别绑定,有先后之分
路由层
协议编(解)码层
传输层
1.4 不足与展望
h1:
队头阻塞:后面的分片要等前面的数据到来才能发送数据
传输效率低:只想传一句话,但实际传的信息有很多
明文传输不安全:
H2:
多路复用
头部压缩
二进制协议
QUIC:
基于UDP实现解决队头阻塞
加密减少握手次数
支持快速启动
2. HTTP
2.1 分层设计
应用层
和用户打交道
提供合理的api:可理解性(命名)、简单性、冗余性(不要重复功能的函数)、兼容性、可测性、可见性
中间件层
需求:
- 配合handler实现一个完整的请处理生命周期
- 拥有预处理逻辑与后处理逻辑
- 可以注册多中间件
- 对上层模块用户逻辑模块易用
洋葱模型
中间件设计
- 既然要实现预处理和后处理,就像调用函数
在中间件里面调用函数 - 用户不主动调用写一个处理函数怎么办
在任何场景下index保证递增(不懂) - 出现异常想停止
index直接等于最大值
路由层
路由框架是为URL匹配对应的处理函数
有静态路由和参数路由
路由修复
冲突路由及优先级
匹配HTTP方法
多处理函数:方便添加中间件
协议层
抽象出合适的接口
网络层(传输)
BIO 阻塞IO
NIO
注册一个监听器,监听器监听到足够的数据之后再唤醒
这样在读数据的时候,就不会因为数据不足而导致阻塞
3. 优化
3.1 针对网络库的优化
go net
需求:
- 存下全部header
header是不知道长度的,要全部存下之后才能进行解析 - 减少系统调用次数
用户态和内核态的切换成本大 - 能够复用内存
- 能够多次读
对一个超大的header,可能一次读不完
go net wiht bufio
绑定一块大小为4k左右的缓冲区
netpoll
需求:
存下全部header
拷贝出完整的body
链表实现buffer:
header可能分到了两个节点上,body也可能
在需要解析的时候,要先把它们拼接起来
解决:
netpoll with nocopy peek
弄一个足够大的节点,能把整个header 和 body放进去的那种
同时要限制节点大小最大值,提高内存利用率
gonet 对流式友好(read靠用户去调,用户不掉,就会一直在tcp缓冲区,不会把内存大炸了),小包性能高
net poll 中大包性能高,时延低
3.2 headers解析
- 找到headerline 边界:\r\n
字符串匹配算法:先找到\n看它前面是不是\r
SIMD:单指令多数据流技术
从一个一个匹配到一次比多个 - 快速解析
通过header key首字母快速筛除掉完全不可能的key
对于常用的key,解析对应value到独立字段
使用byte slice 管理对应 header存储,方便复用
对于普通的header性能会较低
3.3 header key 规范化
表映射的方式
将对应字节转成askii码,在对应的表里查找,然后返回
比采用加减法的效率高,将近40倍,但是有额外的内存开销,对表的变更会困难
热点资源池化
requestcontext,与请求一一对应,贯穿一个请求的始终
高并发场景下,内存压力大
解决:
弄个requestcontext池,当有一个请求到来,从池里取出一个requestcontext用,在返回response之后,把这个requestcontext放回池里面
减少了内存分配,提高了内存复用,减低gc压力,性能提升
但是,把requestcontext放回池里面之后 需要额外的reset逻辑来实现,来给下一个请求复用,以免对下一个请求影响
有一个超出请求声明周期,这个requestcontext就不可靠了,可能会有数据不一致问题,定位难度增加
4. 企业实践
追求性能
追求易用,减少误用
打通内部生态
文档建设、用户群建设
常问的问题写在文档里面