HTTP框架 | 青训营笔记

145 阅读10分钟

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

一:相关基础概念解释

1.1:什么是HTTP?

HTTP是超文本传输协议,拆开来看,就是超文本+传输+协议。归根到底Http就是一个应用层的协议,只不过是用来传输的,传输的内容是超文本。

——什么是超文本?

一开始,互联网并不叫互联网,因为它不能将两台服务器串联起来,数据无法在两台服务器之间传输,输入的信息只能保存在本地。保存的信息通常以“文本”这种简单的形式存在(文本是一种能够被计算机解析的二进制数据包文件)。后来随着互联网的发展,两台服务器之间能够进行数据传输后,人们也不再满足于只能写入文字和传输文字。于是超文本诞生了,超文本包括图片、视频、跳转链接等。

所以,HTTP经典的解释是:HTTP是一份在计算机网络中专门在两台服务器之间传输文字、图片、视频、音频等超文本数据的规范协议。


1.2:引入——计算机网络模型


1.3:HTTP相关协议

- IP协议、TCP协议、DNS服务

关于上述三个协议的具体解释以及与HTTP协议的关系详见:

t.csdn.cn/RH7ws

以下是上述文章内容的部分摘取

- IP协议

IP网际协议位于网络层,几乎所有使用网络的系统都会用到 IP 协议。TCP/IP 协议族中的 IP 指的就是网际协议,"IP"和"IP地址"是不同的,"IP"其实是一种协议的名称。

IP 协议的作用是把各种数据包传送给对方,而要保证确实传送到对方那里,则需要两个重要的条件, IP 地址和 MAC地址。IP 地址指明了节点被分配到的地址,MAC 地址是指网卡所属的固定地址。IP 地址可以和 MAC 地址进行配对。IP 地址可变换,但 MAC地址基本上不会更改。

ip间的通信依赖于mac地址,在信息传输中,有时候由于中转太多,因此会采用ARP协议(解析地址的协议),通过ip地址就可以反查出对应的mac地址。

- TCP协议

TCP 位于传输层,提供可靠的字节流服务。TCP协议为了更容易传送大数据,把大块数据分割成以报文段为单位的数据包进行管理,并且能够确认数据最终是否送达到对方。

为了保证传输的可靠性,TCP协议采用了三次握手的策略。TCP协议把数据包发送出去后,会向对方确认是否成功送达,握手过程中使用了TCP的标志——SYN和ACK。发送端首先发送一个带 SYN 标志的数据包给对方。接收端收到后,回传一个带有 SYN/ACK 标志的数据包以示传达确认信息。最后,发送端再回传一个带 ACK 标志的数据包,代表“握手”结束,这就是三次握手。若在握手过程中某个阶段莫名中断,TCP 协议会再次以相同的顺序发送相同的数据包。

- DNS服务

DNS服务是和 HTTP 协议一样位于应用层的协议。它提供域名到 IP 地址之间的解析服务。

计算机可以被赋予IP地址,也可以被赋予主机名或域名。由于主机名或域名比IP更容易记忆,用户通常使用主机名或者域名来访问对方的计算机,而不是直接使用IP地址访问。DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务,从而实现通过域名或主机名访问的效果。

二:HTTP请求

2.1:HTTP协议的请求报文

先上两张图,在www.baidu.com中搜索“233”的请求和响应报文如下

Http协议主要由三大部分组成:

  • 请求行/状态行(Header) ——描述request/response的基本信息
  • 请求头/响应头(Header) ——使用key-value的形式更详细地说明报文
  • 请求体/响应体(Body) ——实际传输的数据,即消息正文,不一定是文本,可以是超文本

一份Http报文中,可以没有Body,但一定要有Header

上面两张图中,第一行均为请求行/状态行。在Request(请求)中,格式为(方法名+URL+协议版本);在Response中,格式为(协议版本+状态码+状态码描述)。第二行开始均为请求头/响应头,使用k-v对的形式对请求/响应进行描述。

2.2:HTTP常用请求方法汇总

  1. GET: 获取资源
  2. POST: 传输实体主体
  3. HEAD: 获取报文首部
  4. PUT: 传输文件
  5. DELETE: 删除文件
  6. CONNECT: 要求用隧道协议连接代理
  7. OPTIONS: 询问支持的方法
  8. TRACE: 追踪路径

\

2.3:HTTP请求URL

Http协议使用URL定位互联网上的资源,URL的格式如上图,一共分为六个部分

  1. 协议: 告诉浏览器使用何种协议。对于大多数的Web资源,都是使用Http/Https协议
  2. 主机: 即域名,指示需要向网络中的哪一台主机发起请求。当然也可以使用IP地址发起请求(比如向本地127.0.0.1)
  3. 端口: 两台主机发起TCP连接需要“主机+端口”两个条件,缺一不可。但如果访问的Web服务器使用的是Http协议的标准端口(80或443),则URL的端口部分可省略不写
  4. 路径: 跟文件路径的写法类似,表示的是请求主机中的文件路径
  5. 查询: 提供给Web服务器的额外参数,每个k—v对中间由&分隔
  6. 片段: 定位标识符。如果是HTML文档,浏览器会滚动到片段所描述的位置;如果是音频文件,会定位到片段描述的时间

\

2.4:补充——状态码汇总

状态码是一个三位数

  • 1开头:信息类
  • 2开头:表成功

200:成功响应

204:请求处理成功,但是没有资源可以返回

206:对资源的某一部分进行响应,由Content-Range指定范围的实体内容

  • 3开头:重定向

301:永久重定向,表示请求的资源已重新分配URI,以后应当使用资源现有的URI对其进行访问

302:临时重定向,表示请求的资源临时分配了新的URI,用户本次需要使用新的URI进行访问

303:请求的资源存在着另一个URI,应使用GET方法定向获取资源

304:表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但是未满足既定条件

307:与302含义相同

  • 4开头:客户端错误

400:请求的报文存在语法错误

401:发送的请求需要有通过HTTP认证的认证信息

403:表明请求被服务器拒绝了

404:表明服务器上无法找到该请求对应的资源

  • 5开头:服务端错误

500:表明服务器在执行请求时发生了错误

503:表明服务器暂时处于超负载或正在进行停机维护,暂时无法处理请求

2.5:HTTP整个请求响应过程

用户在浏览器输入网址--->DNS服务器对域名进行映射,找到被访问网址所对应的IP地址--->Http进程在80端口发起一个到目标服务器的TCP连接--->Http客户端通过套接字向服务器发送一个Http请求(Request)报文--->Http服务器通过套接字接收Request报文,并对其进行解析--->服务器从存储器/磁盘检索对象--->服务器将检索到的对象封装进Response报文中,并通过套接字向客户端进行发送--->Http服务器通知TCP断开TCP连接(客户端接收到Response报文后才会断开)--->Http客户端把对应资源通过显示器呈现给用户

2.6:HTTPS

Https=Http+SSL,就是Http协议之上加了一个SSL(安全套接字)协议

Http一般是明文传输,易被攻击者窃取到重要信息。而Https是在Http基础之上通过“传输加密和身份认证”保证了传输的安全性。

三:HTTP框架的分层设计

3.1:分层设计图

自上而下分别是:

应用层、中间件层、路由层、协议层(编解码层)、网络层


3.2:应用层设计

——设计规范

应用层需要保证“易用性”,因此需要提供一些合理的API

  1. 可理解性: 使用主流的概念,如ctx.Body()、ctx.GetBody()、避免使用ctx.BodyA()类似的语句
  2. 简单性: 常用的API要放在上层,易误用/低频的API放下层。比如ctx.Request.Header.Peek(key)/ctx.GetHeader(key)
  3. 可见性: 遵循最小暴露原则,不需要暴露的API尽量不暴露,可以抽象为接口
  4. 兼容性: 尽量避免break、change,做好版本管理
  5. 冗余性: 要避免冗余(不要出现能通过其他API得到的API)

\

3.3:中间件层设计

——洋葱模型(中间件典型模型)

简述: 客户端发送请求后,首先要先经过日志中间件的预处理,再经过Metrics中间件的预处理。处理完之后再进行真正的业务逻辑。在这之后,还会有一个后处理的过程。执行完业务逻辑后会先进行Metrics中间件的后处理,再进行日志中间件的后处理

(即日志中间件——>Metrics中间件——>业务逻辑——>Metrics中间件——>日志中间件)

——为什么要中间件?

假设现在需要打印每个请求的request和response

假如没有中间件:

会发现非常繁琐,每写一个业务逻辑都要重新写一遍打印日志的代码

假如有中间件:

可以只写一遍打印日志的代码

这里只有一个中间件,但在现实中,其实是存在多个中间件联合运作的。而多个中间件之间由ctx.Next()进行连接。

——那么问题来了,下图中出现的ctx.Next(),它是如何在多个中间件和真正的业务逻辑代码之间运作的?

ctx.Next(),只能在中间件之间使用,它会挂起当前中间件(也就是ctx.Next()后面的代码先不执行),而先执行后面的中间件,当没有下一个中间件时,就开始执行真正的业务逻辑代码。执行完毕后,再执行每一个中间件ctx.Next()后面的代码(由洋葱模型知,先挂起的后执行)


3.4:路由层设计

  • 静态路由: /a/b/c 、/a/b/d .......
  • 参数路由: /a/:id/c(/a/b/c或/a/d/c或......)
  • 路由修复: /a/b <——>/a/b/(即如果是只注册了/a/b,但是访问的 URI 是 /a/b/,那可以提供自动重定向到 /a/b 能力;同样,如果只注册了 /a/b/,但是访问的 URI 是 /a/b,那可以提供自动重定向到 /a/b/ 能力)
  • 冲突路由: 同时注册 /a/b 和 /:id/b,并设定优先级。比如:当请求 URI 为 /a/b 时,优先匹配静态路由 /a/b

参数路由的设计方法:

——map[string]handlers(一个参数对应一个handers)

——前缀匹配树(如下图)


3.5:协议层(编解码层)设计

任何协议,只要实现了上图中的Server接口,就可以被注册到框架中来

包括http1、http2、QUIC等协议都是实现了上述的Server接口

3.6:网络层设计

有关网络层IO的介绍,详见这篇文章:
t.csdn.cn/gDup7