HTTP框架修炼之道 | 青训营笔记

95 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第6篇笔记

01 再谈HTTP协议

1.1 HTTP协议是什么

超文本传输协议:超体现在不仅能传输文字还能传输图片,视频和连接。

为什么要有协议? 协议就是一个说话方式的规定,大家按这个方式交流,就可以相互接收到消息了。

1.2 协议里面有什么

请求行、请求头、请求体

put方法和patch方法的区别:put的语义是完整更新,patch是部分更新,并且put是幂等的,而patch不是幂等的

1.3 请求的流程

image.png

1.4 不足与展望

  • HTTP1
  1. 队头阻塞
  2. 传输效率低
  3. 明文传输不安全
  • HTTP2
  1. 多路复用
  2. 头部压缩
  3. 二进制协议
  • QUIC
  1. 基于udp实现
  2. 解决队头阻塞的问题
  3. 加密减少握手次数
  4. 支持快速启动

02 HTTP框架的设计与实现

2.1 分层设计

在分层之后,大家只需要使用下一层提供的接口,专注于特定层的开发就可以了。并且分层框架可以让我们更容易做横向扩展。并且分层设计也可以做到很高的复用。

  • 专注性
  • 扩展性
  • 复用性
  • 高内聚低耦合
  • 易复用
  • 高扩展性

image.png

2.2 应用层设计

提供合理的API

  • 可理解性
  • 简单性
  • 冗余性
  • 兼容性
  • 可测性
  • 可见性

2.3 中间层设计

中间件需求:

  • 配合handler实现一个完整的请求处理生命周期
  • 拥有预处理逻辑和后处理逻辑
  • 可以注册多个中间件
  • 对上层模块用户逻辑模块易用

洋葱模型

image.png

适用场景:

  • 日志记录
  • 性能统计
  • 安全控制
  • 事务处理
  • 异常处理
  1. 既然要实现预处理和后处理,那这个就很像调用了一个函数

image.png 2. 路由上可以注册多个middkeware,同时也可以满足请求级别有效,只需要将middleware设计为和handler形同即可。 3. 用户如果不主动调用下一个处理函数怎么办?(核心:在任何场景下保证index递增)

image.png 4. 出现异常想停止怎么办?

image.png

调用链

image.png 适用场景:

  • 不调用next 初始化逻辑且不需要在同一调用栈
  • 调用next 后处理逻辑或需要在同一调用栈上

2.4 路由设计

框架路由其实是为url匹配对应的处理函数(handler)

  • 静态路由:a/b/c a/b/d
  • 参数路由: a/:id/c
  • 路由修复: /a/b/ <-> /a/b
  • 冲突路由以及优先级
  • 匹配HTTP方法
  • 多处理函数:方便添加中间件

青铜 : map[string]handlers

黄金:前缀匹配数(如何处理带参数的路由注册?)

如何匹配HTTP方法?

image.png

如何实现添加多个处理函数? 在每个节点上使用一个list存储handler

image.png

2.5 协议层设计

抽象出合适的接口:

image.png 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 paramter. 需要在连接上读写数据

2.6 网络层设计

  • BIO

image.png

  • NIO

image.png

go net bio用户用户管理buffer

image.png

netpoll nio 网络库管理buffer

image.png

image.png

03 性能修炼之道

3.1 针对网络库优化

go net 存下全部header 减少系统调用次数 能够复用内存 能够多次读

go net with bufio 绑定一块缓冲区,因为大部分时候包都是在4k以下,所以可以给每一个链接绑定一个4k大小的缓冲区,然后再提供一些方法来操作这个缓冲区。

image.png

peek不移动指针,discard移动指针,release复用内存

go net 流式友好,小包性能高 netpoll 中大包性能高,时延低

3.2 针对协议的优化

3.2.1 Headers解析

找到header line边界 用SIMD

针对协议相关的Headers解析

  1. 可以通过首字母进行快速的过滤
  2. 解析对应value到独立字段
  3. 使用 byte slice进行header存储和管理,方便复用

取:

  • 核心字段快速解析
  • 使用byte slice存储
  • 额外存储到成员变量中 舍:
  • 普通header性能低下
  • 没有map结构

3.2.2 Header key规范化

空间换时间的一个优化,把字母表提前存好。

取:

  • 超高的转换效率
  • 比net.http提高40倍

舍:

  • 额外的内存开销
  • 变更困难

3.3 热点资源池化

image.png

取:

  • 减少了内存分配
  • 提高了内存复用
  • 降低了gc压力
  • 性能提升

舍:

  • 额外的Reset逻辑
  • 请求内有效
  • 问题定位难度增加

04 企业实践

  • 追求性能
  • 追求易用,减少误用
  • 打通内部生态
  • 文档建设、用户群建设