HTTP 框架修炼之道 | 青训营

92 阅读3分钟

课程背景

HTTP 框架负责的就是图中红色圈部分

image1.png

一、再谈 HTTP 协议

1、HTTP协议是什么

HTTP:超文本传输协议(Hypertext Transfer Portocol)

image2转存失败,建议直接上传图片文件

为什么需要协议?

  1. 需要明确的边界
    • 开始
    • 结束
  2. 能够携带信息
    • 什么消息
    • 消息类型
    • .......

image3转存失败,建议直接上传图片文件

2、协议里有什么

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

请求:

image4转存失败,建议直接上传图片文件

响应:

image5转存失败,建议直接上传图片文件

总结:

请求的格式

  1. 请求行
    • 方法名
      • 常见方法名:GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH
    • URL
    • 协议版本
  2. 请求头
    • 协议约定
    • 业务相关
  3. 请求体

响应的格式

  1. 状态行
    • 协议版本
    • 状态码
      • 1xx:信息类
      • 2xx:成功
      • 3xx:重定向
      • 4xx:客户端错误
      • 5xx:服务端错误
    • 状态码描述
  2. 响应头
    • 协议约定
    • 业务相关
  3. 响应体

接下来用代码实现上面的小 demo:

image6转存失败,建议直接上传图片文件

package main

import (
	"context"
	"code.byted.org/middleware/hertz/pkg/"
	"code.byted.org/middleware/hertz/pkg/app"
)

func main() {
	h := server.new()

	h.POST("/sis", func(c context.Context, ctx *app.RequestContext) {
		ctx.Data(200, "text/plain; charset=utf-8", []byte("OK"))
	})

	h.Spin()
}

3、请求流程

一次请求的完整流程:

image7转存失败,建议直接上传图片文件

4、不足与展望

1、HTTP1 存在的问题:

队头阻塞:后续的分片必要等待前面的分片的到来,才能继续发送后面的数据,否则会一直等待

传输效率低:无用信息多,甚至可能会多于请求体的内容

不支持多路复用:一个请求未结束前,该连接上不能发送其他请求

明文传输不安全

2、HTTP2 协议:

支持多路复用和头部压缩

采用二进制协议,解析起来更高效

但未解决队头阻塞问题

3、QUIC 协议:

基于 UDP 实现

解决了队头阻塞问题

优化加密算法,减少握手次数

支持快速启动

二、HTTP 框架的设计与实现

1、分层设计

HTTP 框架的设计应满足专注性、拓展性、复用性

image8转存失败,建议直接上传图片文件

HTTP 框架设计时需要考虑的一些点:

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

image9转存失败,建议直接上传图片文件

一个切实可行的复杂系统势必是从一个切实可行的简单系统发展而来的。从头开始设计的复杂系统根本不切实可行,无法修修补补让它切实可行。你必须由一个切实可行的简单系统重新开始。————盖尔定律

2、应用层设计

用户层是和用户直接打交道的一层,其核心是提供合理的 API

设计 API 时需要考虑的一些点:

  • 可理解性:如 ctx.Body()ctx.getBody(),不要用 ctx.BodyA()
  • 简单性:如获取 Header 中某个字段的方法 ctx.Request.Header.Peek(key),由于使用频繁,可封装成更简单的方法 ctx.GetHeader(key)
  • 冗余性:不要有两个接口在完成相同的事情,或者一个接口是由两个接口拼起来完成的
  • 兼容性
  • 可测性
  • 可见性

不要试图在文档中说明,很多用户不看文档

3、中间件设计

中间件需求:

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

一个经典的中间件模型——洋葱模型:

image10转存失败,建议直接上传图片文件

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

适用场景:

  • 日志记录
  • 性能统计
  • 安全控制
  • 事务处理
  • 异常处理

举个例子:打印每个请求的 request 和 response

没有中间件时,需要在每个接口的开始和结束分别加上打印 request 和 response 的两句话

三、性能修炼之道

四、企业实践