HTTP框架设计指标(入门级)| 青训营

70 阅读4分钟

HTTTP 设计模型


参考 视频

基础

概念模型

模型和rpc很相似,多了一个路由层

关于协议版本的问题可以看看小林的区别

image-20230802144521213

无视底层

image-20230802144741158

具体设计

啊,我的天,设计一个http框架,

  • 首先就要考虑怎么DispatchServlet:根据路由进行特定方法:路由设计
  • 连接对象的创建、数据请求响应: read、write:网络层设计
  • 拦截器:中间件设计
  • 报文解析、压缩:协议层

image-20230802145348630

应用层设计

提供合理的APi

  • 可理解性:如ctx.Body(),ctx.GetBody() 冗余性 不要用ctx.BodyA() 兼容性
  • 简单性:如ctx.Request.Header.Peek(key) 可测性 /ctx.GetHeader(key) 可见性

中间件设计

中间件需求:

  • 配合Handler实现一个完整的请求处理生命周期

    • handler:处理业务
    • 中间件:拦截器前后处理逻辑
  • 拥有预处理逻辑与后处理逻辑

  • 可以注册多中间件

  • 对上层模块用户逻辑模块易用

如洋葱模型

image-20230802145901671

拦截器

就像是拦截器一样使用context

image-20230802150206024

  1. 既然要实现预处理和后处理,那这个就很像调用了一个拦截器
  2. 路由上可以注册多Middleware,同时也可以满足请求级别有效,只需要将Middleware设计为和业务和Handler相同即可。 就是调用下一级的函数时使用, ctx.next()调用下一级:无论是多级拦截还是调用hander都这样写
  3. 用户如果不主动调用下一个Middleware 怎么办?

image-20230802151009815

核心:在任何场景下index保证递增,自动调用下一个Middleware

  1. 出现异常想停止怎么办?

    image-20230802151057493

调用链

image-20230802151410609

go的协程并不会共享同一个栈,因此有其他问题

适用场景

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

路由设计

框架路由实际上就是为URL匹配对应的处理函数(Handlers)

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

路由设计

  • map[string].handlers /a/b/c、/a/b/d/a/:id/c、/*al
  • 前缀匹配树 /a/b/c、/a/b/d如何处理带参数的路由注册? (处理形如:/a/:id/b类型的路由)

image-20230802152322195

匹配HTTP方法

路由映射表

image-20230802152405084

外层Map:根据method进行初步筛选

实现添加多处理函数:在每个节点上使用一个Iist存储handler

协议层

type Server interface {
    Serve(c context.Context, conn network.Conn) error
}
  1. 请求参数

    不要将Contexts存储在结构类型内部;相反,将Context显式传递给每个需要它的函数。Context应该是第一个参数。

  2. 连接对象

    需要在连接上读写数据

网络层设计

BIO

go func(){
    for {
        conn,_listener. Accept() //对于每个连接开一个协程
        go func (){
            conn.Read(request)
            handle...           //阻塞
            conn.Write(response)
        }()
    }
}()

NIO

go func(){
    for {
        readableConns,_:=Monitor(conns)//注册监听器,当有一定的数据才唤醒
        for conn range readableConns{
            conn,_listener. Accept() //对于每个连接开一个协程
            go func (){
                conn.Read(request)
                handle...           //阻塞
                conn.Write(response)
            }()
        }
    }
}()

接口设计

type Conn interface {
    Read(b []byte)(n int,err error)
    Write(b []byte)(n int,err error)
}

HTTP优化指标

网路库

就是网络层的设计,如请求响应的Read方法、Write方法、连接对象的处理

优化方向:

  • 存下全部Header
  • 减少系统调用次数
  • 能够复用内存
  • 能够多次读

1、go net

go的net默认使用网路库模型BIO,每次buf不够才会扩容

2、go net with bufio:绑定一块缓冲区

3、netpoll

  • 存下全部Header
  • 拷贝出完整的Body

4、netpoll with nocopy peek

  • 分配足够大的buffer
  • 限制最大buffer size

网络库

针对协议的优化

Headers解析

Header key规范化

aaa-bbb->Aaa-Bbb

热点资源池化

找到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

针对协议相关的Headers快速解析:

1.通过Header key首字母快速筛除掉完全不可能的key 2.解析对应value到独立字段 3.使用byte slice管理对应header存储,方便复用

请求体中同样处理的Key:User-Agent、Content-Type、Content-Length、Connection、Transfer-Encoding