HTTP框架的基础设计与实现 | 青训营

69 阅读2分钟

写在前面

由于还没有学过《计算机网络》这门课程,在实践时面对网络传输相关的代码总是发怵。今天就让我来恶补一下相关知识。

关于HTTP协议的基础知识

1.HTTP协议是什么?

HTTP的英文全称是Hypertext Transfer Ptotocal,直译过来就是超文本传输协议。在常见的开发中,HTTP协议是连接前后端的重要桥梁。 图片.png

2.HTTP协议的基础框架是什么样子?

图片003.png

3.一个常见的POST请求在协议层做了什么?

客户端:

POST/sis HTTP/1.1
//请求行
//方法名(POST),URL(sis HTTP),协议版本(1.1)

Who:Alex
Content-Type:text/plain
Hoist:127.0.0.1:8080
//请求头

Let's watch a movie togethor!
//请求体

服务端:

HTTP/1.1 200 OK
//状态行
//HTTP版本号(1.1),状态码(200),对于状态码的描述(OK)

Server:hertz
Date:Thu,21 Apr 2022 11 11:46:32 GMT
Content-Type:text/plain;charset=utf-8
Content-Length:2
Upstream-Caught:165054159283580
//响应头

OK
//相应体

HTTP的各层具体设计

应用层设计

这是与用户直接打交道的一层,对于用户的请求进行抽象,提供丰富易用的API。 在设计时应该注重一下几点:

  • 可理解性:如ctxBody(),不要用ctx.BodyA()
  • 简单性:如ctx.GetHeader(key)
  • 冗余性
  • 兼容性
  • 可测性
  • 可见性
中间件层设计

这一层对于用户有预处理和后处理的逻辑。 在设计时应该要注意以下几点:

  • 配合Handlers层实现一个完整的请求处理生命周期
  • 拥有预处理逻辑和后处理逻辑
  • 可以注册使用多个中间件
  • 对于上层模块用户逻辑模块易用
中间件设计(洋葱模型)
  1. 既然要实现预处理和后处理,那这个就很像调用了一个函数
func Middleware(some param) {
       // some logic for pre-handle
……
      nextMiddleware ( ) / bizlogic ( ) #可以统一变为next()

      // some logic after-handle
……
}
  1. 路由上可以注册多个 Middleware,同时也可以满足请求级别有效。
    只需要将Middleware设计为和业务和handler相同即可

  2. 用户如果不主动调用下一个函数怎么办?帮助用户主动实现

func (ctx *RequegstContext) Next() {
       ctx.index++
      for ctx.index < int8(len(ctx.handlers)) {
           ctx.handlers[ctx.index]()
           ctx.index++
      }
}

核心:在任何场景下index保持递增

  1. 出现异常想停止怎么办?
func (ctx *RequegstContext) Abort() {
      ctx.index = IndexMax
}
  1. 不同场景的中间件调用方式不同 不调用Next:初始化逻辑且不需要在同一调用栈 调用Next:后处理逻辑或需要在统一调用栈上
路由层设计(前缀匹配树)

这一层主要用来注册、寻址等。框架路由实际上是为了URL匹配对应的处理函数(Handlers)

  1. 静态路由:/a/b/c,/a/b/d
  2. 参数路由:/a/:id/c (/a/b/c,/a//d/c), /*all
  3. 路由修复:/a/b <-> /a/b/
  4. 冲突路由以及优先级: /a/b, /:id/c
  5. 匹配HTTP方法
  6. 多处理函数:方面添加中间件
协议层设计
Type Server interface {
       Serve(c context.Context, conn network.Conn) error
}

抽象出合适的接口

  1. 不要把Context存储到结构体当中,相反,应该把Context精准地传递到每一需要的function上去。同时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 {
                       go func(){
                             conn.Read(request)

                            handle……
 
                     conn. Write (response)
                       }()
                 }
          }
}()