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

71 阅读3分钟

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

HTTP协议

HTTP协议是什么

Hypertext Transfer Protocol超文本传输协议

图片,音乐、视频、超链接等资源超出了文本范围

需要明确的边界,消息类型等

协议有什么-POST为例

请求行/状态行

请求头/响应体

请求体/相应体

常用方法名:GET\HEAD\POST\DELETE\CONNECT\OPTIONS\TRACE\PATCH

请求流程

不足和展望

HTTP1:

队头阻塞问题

传输效率低

明文传输不安全

HTTP2:

多路复用

头部压缩

二进制协议

QUIC:

基于UDP实现、解决队头阻塞

加密减少握手次数

支持快速启动

HTTP框架的设计与实现

高内聚、低耦合、易复用、高扩展性

应用层设计

提供合理的API

  • 可理解性:如ctx.Body()
  • 简单性:如ctx.GetHeader(key)而不是 ctx.Request.Header.Peek(key)
  • 非冗余性:不要有接口冗余
  • 兼容性
  • 可测性:接口可测试
  • 可见性:保证安全

中间件设计

需求

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

设计

请求经过日志、metric、业务逻辑、metric、日志

  1. 实现预处理和后处理
  2. 路由上可以注册多Middleware,同时也可以满足请求级别有效只需要将Middleware设计为和业务和handler相同即可。
func Middleware (some param){
    //pre-handle
    ...
    Next()
    ...
    //after-handle
}
  1. 帮助用户调用下一个处理函数
func (ctx *RequestContext) Next(){
    ctx.index++
    for ctx.index <int8(len(ctx.hanlders)){
        ctx.handlers[ctx.index]()
        ctx.index++
    }
}
  1. 出现异常要停止
func(ctx *RequestContext) Abort(){
    ctx.index = IndexMax
}

将index设为最大值,直接跳出循环

调用链:

路由设计

框架路由就是为URL匹配对应的处理函数(handlers)

静态路由:/a/b/c

参数路由:/a/:id/c /*all

路由修复: /a/b <->/a/b/

冲突路由以及优先级: /a/b /:id/c

匹配HTTP方法

多处理函数:方便添加中间件

  • map[string]handlers但是不适合参数路由
  • 前缀匹配树:
  • 参数路由

如何匹配HTTP方法

多处理函数的处理

在每个节点上使用一个list存储handler

协议层设计

抽象出合适接口

网络层设计

BIO

NIO

go net

BIO 用户管理buffer

netpoll

NIO网路库管理buffer

github.com/cloudwego/n…

性能优化

网络库优化

go net

存在全部header

减少系统调用次数

能够复用内存

能够多次读

go net with bufio

绑定一块缓冲区

优势

流式友好

小包性能高

netpoll

存在全部的header,拷贝出完整的Body

netpoll with nocopy peek

如下图,存在跨节点的问题

分配足够大的buffer

限制最大的buffer size

优势

中大包性能高

时延低

针对协议的优化-headers解析

找到Header Line边界:\r\n,先找到\n再看它前面一个是不是\r

func index(b [] byte,c byte) int {
    for i :=0; i<len(b); i++{
        if b[i] == c {
            return i
        }
    }
    return -1
}

更快的方式SIMD,单指令多数据流,一次处理多个

核心字段快速解析、使用byte slice存储、额外存储到成员变量中

header key规范化

使用table存储大小写映射,O(1)复杂度即可转化大小写,比ASCII码加减效率提高40倍

,但是引入了额外的内存开销,且变更困难,两张表可能需要改

热点资源池化

Request Context池子,不同请求可以复用

减少了内存分配次数

降低GC压力

性能提升

但是引入额外的reset操作,防止前面请求对后面有影响

请求内有效

问题定位难度高