HTTP协议介绍
协议的含义
超文本传输协议(Hypertext Transfer Protocol)
协议的作用
- 举个栗子🍘:人在说话的时候需要一定的语法、主谓宾去造句,这样对方才能理解说话人想要表达的含义。同理,在网线上传输的都是比特流(0/1数据流),需要使用一定的规则才能转化为可理解的数据并在网络间传播。
- 所以协议需要:
- 明确的边界:开始、结束
- 能够携带信息:什么消息、消息类型
协议的内容
- 请求行/状态行:
- 请求行:方法名(如GET、HEAD、POST、PATCH等)、URL、协议版本
- 状态行:协议版本、状态码(1xx:信息类、2xx:成功、3xx:重定向、4xx:客户端错误、5xx:服务端错误)、状态码描述
- 请求头/响应头:协议相关(指定字段有多少个字节等)+业务相关(源数据)
- 请求体/响应体:具体业务内容
请求流程
类似OSI七层网络模型,可将请求流程分为以下五层(从左到右依次深入): 业务层->服务层+中间件层->路由层->协议编(解)码层->传输层
不足与展望
- HTTP1:基于TCP实现,会出现队头阻塞、传输效率低、明文传输不安全的缺点
- HTTP2:多路复用、头部压缩、二进制协议,但由于仍基于TCP,没有解决队头阻塞的问题
- QUIC:基于UDP实现,可解决队头阻塞,加密减少握手次数,支持快速启动
HTTP框架的设计与实现
分层设计
- 专注性:高内聚、低耦合
- 扩展性:高扩展性
- 复用性:易复用
分层实现——实例
分为五层(还有一层是并行的),层与层之间使用接口解耦,从上到下分别是
- Application(应用)层:提供应用API,直接与用户交互
- middleware(中间件)层:对用户进行预处理和后处理的逻辑
- route(路由)层:原生路由实现
- codec(协议)层:实现通信
- transport(网络)层:不同网络的使用场景互不相同 (commons层:放置一些公共逻辑,每一层都会调用)
应用层设计
提供合理的API:可理解性、简单性,去除冗余性、兼容性
中间件设计
中间件需求:
- 配合Handler实现一个完整的请求处理生命周期
- 拥有预处理逻辑与后处理逻辑
- 可以注册多中间件
- 对上层模块用户逻辑模块易用
经典中间件模型
洋葱模型:日志->Metrics->Biz Handler(预处理和后处理,处理请求与发送回复)
- 适用场景:日志记录、性能统计、安全控制、事务处理、异常处理
路由设计
框架路由实际上就是为URL匹配对应的处理函数(Handlers),包括静态路由(/a/b/c)、参数路由(/a/:id/c)等。
- 使用map[string]handlers:快和简单,但只能匹配静态路由
- 前缀匹配树:可以匹配静态路由,参数路由
匹配HTTP方法
外层Map:在路由映射表中,根据method进行初步筛选
添加多处理函数
在每个节点上使用一个list存储handler
查找路由
明确需求、业界调研、方案权衡、方案评审、确定开发
协议层设计
核心是抽象出合适的接口。
网络层设计
BIO:一旦建立连接就启动方法读取数据,读数据读到一半卡住了就会暂停直至超时。 NIO:注册一个监听器,监听器读取到一定的数据后就会唤醒相应的方法。(整个流程没有阻塞)
Golang的标准库——go net
是一个典型的BIO,有两个关键接口Read和Write。需要由用户管理Buffer。
netpoll网络库
采用NIO模式,有两个关键接口Reader和Writer,Writer底下也包括两个接口Malloc(开辟空间,拷贝数据)和Flush(数据发送出去,清理空间)。需要网络库管理Buffer。
性能修炼之道
针对网络库的优化
❓go net的改进需求:存下全部Header、减少系统调用次数、能够复用内存、能够多次读 👉改进方法:go net with bufio(绑定一块缓冲区) ❓netpoll的改进需求:存下全部Header、拷贝出完整的Body 👉改进方法:netpoll with nocopy peek(分配足够大的buffer、限制最大buffer size)
不同网络库优势
- go net:流式友好、小包性能高
- netpoll:中大包性能高、时延低
针对协议的优化
Headers解析
- 找到Header Line边界:\r\n,时间复杂度:O(n) 👉提升速度方法:SIMD:单指令流多数据流(一个指令能够同时处理多个数据)
- 针对协议相关的Headers快速解析
- 通过Header key首字母快速筛除掉完全不可能的key
- 解析对应value到独立字段
- 使用byte slice管理对应header存储,方便复用
- 优点:核心字段快速解析;使用byte slice存储;额外存储到成员变量中
- 缺点:普通header性能较低;没有map结构
Header key规范化
采用表映射的方式,将对应的字节转化为ASCII码,在对应的表里进行匹配查找并返回。
- 优点:超高的转换效率;比net.http提高40倍
- 缺点:额外的内存开销;变更困难
热点资源池化
RequestContext(包括Request、Response和Data)和请求一一对应、贯穿一个请求始终。 👉 将所有RequestContext放入RequestContext池里。
- 优点:减少内存分配;提高内存复用;降低GC压力;性能提升
- 缺点:额外的Reset逻辑;请求内有效;问题定位难度增加
企业实践
- 追求性能
- 追求易用,减少误用
- 打通内部生态
- 文档建设、用户群建设(问题沉淀)