1.HTTP协议
- HTTP协议是什么
- 协议里有什么
- 请求流程
- 不足与展望
1.1 HTTP 协议是什么
HTTP:超文本传输协议(Hypertext Transfer Protocol) ,基于TCP协议的应用层传输协议,一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)
1.1为什么需要协议
1.2协议里有什么
常见方法名
- GET 向指定的资源发出“显示”请求。使用GET方 法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,例如在Web Application中。其中一个原因是GET可能会被网络蜘蛛等随意访问。
- HEAD 与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)。
- POST 向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。
- PUT 向指定资源位置上传其最新内容。
- DELETE 请求服务器删除Request-URI所标识的资源。
- TRACE 回显服务器收到的请求,主要用于测试或诊断。
- OPTIONS 这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。
- CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。
- patch 约定更新整个资源用put,更新资源的部分内容用patch; PUT 是幂等的,而 PATCH 不是幂等的
状态码
当客户端向服务器端发送请求时,描述返回的请求结果。
- 2XX成功 请求被正常处理了 200 OK: 204 No Content:服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分 206 Partial Content:客户端进行了范围请求,而服务器成功执行了这部分的GET 请求
- 3XX重定向 浏览器需要执行某些特殊的处理以正确处理请求 301 Moved Permanently:永久性重定向,请求的资源已被分配了新的 URI,以后应使用资源现在所指的 URI 302 Found:临时性重定向,请求的资源已被分配了新的 URI,希望用户(本次)能使用新的 URI 访问 303 See Other:由于请求对应的资源存在着另一个URI,应使用_GET_方法定向获取请求的资源 304 Not Modified:表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但未满足条件的情况(和重定向没有关系)。
- 4XX客户端错误 400 Bad Request:请求报文中存在语法错误,需修改请求的内容后再次发送请求 401 Unauthorized:发送的请求需要认证信息(第一次),若之前已进行过 1 次请求,则表示用 户认证失败。 403 Forbidden:对请求资源的访问被服务器拒绝了,可以在实体的主体部分对原因进行描述 404 Not Found:服务器上无法找到请求的资源,或者服务器端拒绝请求且不想说明理由
- 5XX服务器错误 500 Internal Server Error:服务器端在执行请求时发生了错误,也可能是Web应用存在的 bug 或某些临时的故障 503 Service Unavailable:服务器暂时处于超负载或正在进行停机维护,无法处理请求
报文首部
HTTP 协议的请求和响应报文中必定包含 HTTP 首部,首部内容为客户端和服务器分别处理请求和响应提供所需要的信息。
HTTP 请求报文:
HTTP 响应报文:
首部字段
给浏览器和服务器提供报文主体大小、所使用的语言、认证信息等内容。 首部字段是由首部字段名和字段值构成的,中间用冒号“:” 分隔。 首部字段类型:
- 通用首部字段(General Header Fields): 请求报文和响应报文两方都会使用的首部。
- 请求首部字段(Request Header Fields):从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信信息、响应内容相关优先级等信息。
- 响应首部字段(Response Header Fields):从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息。
- 实体首部字段(Entity Header Fields):针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息。
demo
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
)
func main() {
hertz := server.New()
hertz.POST("/sys", func(c context.Context, ctx *app.RequestContext) {
ctx.Data(200, "text/plain;charset=utf-8", []byte("ok"))
})
hertz.Spin()
}
将对应的路由注册到 server 上,选择对应的方法,正如图上所示。接下来实现业务逻辑,回复一个 OK。
1.3请求流程
1.4不足与展望
HTTP + 加密 + 认证 + 完整性保护 = HTTPS 通常,HTTP 直接和 TCP 通信。当使用 SSL时,则演变成先和 SSL通信,再由 SSL和 TCP 通信了。采用 SSL后,HTTP 就拥有了 HTTPS 的加密、证书和完整性保护这些功能
HTTP1:
- 首先对于 http1来说,因为它是基于 TCP 。基于 TCP 的都会有队头阻塞的问题,后续的分片必须要等待前面的分片的到来才能继续发送后面的数据,否则的话会一直等待。
- 第二个是他的传输效率很低,但是这里面的无用的信息其实非常的多,存在很多重复的头部什么的。
- http1不支持多路复用,一个请求没结束之前是不能再发送其他请求的。
- 明文传输不安全
HTTP2: HTTP2 解决了HTTP1一部分,但没有完全解决。可以多路复用,二进制协议解析起来更加高效。但是由于 HTTP2 还是基于 tcp 的并没有解决队头阻塞的问题,而且握手的开销也没有优化。
QUIC 在 UDP 就基础上解决得上刚刚的两个问题。
2.HTTP框架的设计与实现
- 专注性
- 扩展性
- 复用性
HTTP框架聚焦于第四层之上。分层的设计可以简化系统设计,让不同的人专注做某一层次的事情。如果系统没有分层,那相关的扩展就会变得不太容易。分层之后可以做到很高的复用。
2.1分层设计
在进行分层设计时,我们需要考虑高内聚低耦合,复用性、扩展性等等。从上往下总共分为了五层,层与层之前使用接口解耦:
- 应用层,这一层的话其实就是跟用户直接打交道的一层,这一层会对请求进行一个抽象,包括像 request response context 等等。这一层也会提供一些丰富的易用的API 。
- 中间件层,可以对请求有一些预处理和后处理的逻辑,像我们可以打一些 accesslog,打一些耗时的点。其他中间件比如 Reacovery 中间件用于捕获 Panic。
- 路由层,会有一个原生的路由实现来提供大家类似于跟注册、路由寻址的一些操作。这一块的话在我们下一部分会具体进行详细地展开。
- 协议层。现在 http1.1 已经不能够满足我们所有的需求了,我们需要支持H2、Quic 等等,甚至是在 TLS 握手之后的 ALPN 协商升级操作,那这些都需要能够很方便的支持。
- 网络层,不同的网络库使用的场景并不相同。
- Common层主要放一些公共逻辑,这一部分可能每一层都会使用。
2.2应用层设计
易用性首先是要提供一些合理的 api:
- 可理解性:使用主流的概念,如 ctx.Body(), ctx.GetBody(),不要用 ctx.BodyA()
- 简单性:常用的 API 放到上层,误用/低频 API 放到下层,如 ctx.Request.Header.Peek(key)/ctx.GetHeader(key)
- 可见性:最小暴露原则,不需要暴露的 API 不暴露,可以抽象为接口。
- 冗余性:不需要冗余或能通过其他 API 组合得到的 API 。
- 兼容性:尽量避免 break change,做好版本管理。
2.3中间件设计
中间件需求:
- 配合Handler 实现一个完整的请求处理生命周期
- 拥有预处理逻辑与后处理逻辑
- 可以注册多中间件
- 对上层模块用户逻辑模块易用
洋葱模型:
预处理:首先经过一个日日志中间件的预处理之后经过 metrics 中间件的预处理,在处理完了之后我们再进行执行一个真正的业务逻辑。
后处理:退出业务逻辑之后,会有一个后处理。首先经过一个 metrics 中间件的后处理,最后是经过一个日志中间件的后处理。然后再将真正的响应再将一个完整的响应返回给用户。这个的中间件的核心它是能够将核心逻辑与通用逻辑分离。适用的场景包括说日志记录、性能统计、安全控制、事务处理、异常处理等等。
调用 Next 和 不调用 Next 的适用场景:一次注册了 ABC 三个中间件和最后一个业务 handler 的调用链图,其中 B 中间件中不调用 next 对,中间件 C 调用 next 。那我们的调用顺序就是首先中间件A去调用中间件B,返回了之后中间件A去调用中间件C,然后中间件C去调用业务Handler,最后返回,也就是按照图上的标号调用。
2.4路由设计
框架路由实际上就是为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方法
- 多处理函数:方便添加中间件
- …
前缀匹配树: