go 后端笔记7 HTTP 协议框架

67 阅读2分钟

HTTP 协议

协议的要素包括:

  1. 明确的边界(开始、结束)
  2. 携带的信息(信息类型、内容)

HTTP 协议框架设计与实现

分层设计

应用层 Application

提供合理的 API

  • 可理解性:如 ctx.getBody()
  • 简单性:如 ctx.GetHeader(name) 而不是 ctx.Request.Header.Peek(name)
  • 冗余性:减少冗余接口
  • 兼容性
  • 可测性:可测试
  • 可见性:保证安全性

中间件 Middleware

将核心逻辑与通用逻辑分离

  • 配合 Handler 实现完整请求处理生命周期
  • 预处理、后处理逻辑
  • 支持多中间件
  • 对上层模块用户逻辑模块易用

  1. 使用 next() 之类统一方法调用下一个中间件:后处理逻辑、在同一个调用栈上的逻辑
  2. 主动帮助用户调用下一个中间件:初始化逻辑,在不同调用栈上的逻辑
  3. 停止之后的中间件调用:

路由层 Route

  • 静态路由
  • 参数路由::*
  • 路由修复:自动识别末尾是否存在 / 并修复
  • 优先级处理
  • 匹配 HTTP 方法(GET,POST...)
  • 多处理函数:便于添加中间件

实现方式:

  • map
    • 快,简单
    • 不支持参数路由
  • 前缀匹配树

协议层 Codec

核心:抽象出合适的接口

Pasted image 20241117031824.png

  • 不存储上下文,由上层传入上下文
  • 需要在连接上写入数据
  • 需要给用户感知错误

网路层 Transport

选择 BIO、NIO

公共层

存放公共逻辑

HTTP 框架优化

针对网络库的优化

  • go net:存下全部 Header,减少系统调用,复用内存,多次读
    • 绑定一个 bufio
  • netpoll:存下完整 Header,Body
    • 分配足够大的 buffer,先在底层将请求拼装好
    • 限制最大的 buffer

底层包的选择:

  • go net:针对流式友好(Read 由用户调用),对小包性能友好(连接上绑定了一个小内存)
  • nepoll:对中大包性能好,时延低

针对协议的优化

Header

  • 找到边界:两个连续的 \r\n,使用 SIMD(go 自动使用)
  • 解析 Header:使用首字母筛掉不可能的 key
    • 使用 byte slice 存储
    • 对于常用的 Header 对应到独立的字段
    • 缺点:没有 map 结构,不常用的 Header 比较难用
  • HeaderKey 规范化(大小写):表映射

热点资源池化

维护一个 RequestContext 池,减少 GC 压力,提高内存复用

  • 额外 reset 操作
  • 请求外数据不可靠
  • 问题定位难度增加(低并发难以复现)

企业实践

实践中,框架追求(权衡)的东西:

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