Http协议与Http框架 | 青训营;

94 阅读12分钟

Http协议与Http框架

1.什么是Http协议

1.1 http协议

HTTP(Hypertext Transfer Protocol)是一种用于在计算机网络上传输超文本的应用层协议。它是万维网的基础,并被用于在Web浏览器和Web服务器之间传输数据。

HTTP使用客户端-服务器模型,其中客户端发送HTTP请求,而服务器返回HTTP响应。客户端可以是Web浏览器、移动应用或其他任何能够发送HTTP请求的程序,而服务器是存储和处理资源的主机。

HTTP协议的主要特点包括:

  1. 无连接性:每个HTTP请求和响应都是独立的,服务器在处理完请求后立即关闭连接。这种无连接性确保了协议的轻量级性。
  2. 无状态性:HTTP协议本身不会在多次请求之间保持状态信息。每个请求都是相互独立的,服务器不会知道之前的请求和响应历史。为了实现状态管理,通常使用一些机制,如Cookies和Session。
  3. 基于请求-响应模型:HTTP是一个请求-响应协议,客户端发送一个HTTP请求,服务器收到请求后进行处理,并返回一个HTTP响应。
  4. URL定位资源:HTTP使用统一资源定位符(URL)来标识和定位Web资源。客户端使用URL来指定所需的资源。
  5. 支持多种请求方法:HTTP定义了多种请求方法,最常见的是GET和POST。GET用于获取资源,而POST用于提交数据到服务器。
  6. 状态码:HTTP响应包含一个状态码,用于指示请求的处理结果,如200表示成功,404表示未找到资源等。
  7. 可扩展性:HTTP协议是可扩展的,可以通过添加新的头部字段或扩展请求方法来满足不同的需求。

HTTP协议通常在TCP/IP协议族上运行,使用端口号80(HTTP)或443(HTTPS)来进行通信。HTTPS是HTTP的安全版本,它在HTTP的基础上添加了SSL/TLS协议,用于加密数据传输,提供更安全的通信。

1.2 Go的一些http框架

  1. net/http: 这是Go语言标准库中内置的HTTP包,提供了基本的HTTP服务器和客户端功能。虽然它是简单且易于使用的,但它不是一个全功能的框架,缺少一些高级功能。
  2. Gin: Gin是一个轻量级的Web框架,具有出色的性能和高度的生产力。它基于net/http包构建,但提供了更多的功能,如中间件、路由组、JSON验证等。由于其速度快和易于使用,Gin成为了Go语言开发者中非常受欢迎的框架之一。
  3. Echo: Echo是另一个流行的轻量级Web框架,它基于标准库的net/http包,并提供了一组易于使用的特性。Echo的设计目标是尽量减少样板代码,使开发更加简单和快速。它支持中间件、路由、HTTP处理器和模板渲染等功能。
  4. Beego: Beego是一个全功能的Web框架,具有MVC架构,适用于构建大型和复杂的应用程序。它提供了丰富的功能,包括路由、ORM(对象关系映射)、验证、会话管理等。Beego支持模块化开发,并提供了开箱即用的工具和插件。
  5. Iris: Iris是一个高性能的Web框架,被称为“最快的Go语言Web框架”。它具有强大的路由功能、中间件支持和高度的灵活性。Iris提供了可选的DI(依赖注入)容器和多语言模板渲染。
  6. Revel: Revel是一个全功能的MVC Web框架,旨在促进快速开发和易于扩展。它提供了许多开箱即用的功能,如路由、ORM、模板渲染、会话管理等。
  7. Fiber: Fiber是一个基于快速HTTP引擎Fasthttp的Web框架,具有非常高的性能。它支持中间件、路由、模板渲染和WebSocket等特性。

1.3 http的方法

  1. GET:用于从服务器请求指定的资源。GET方法应该只用于获取数据,并且应该是安全和幂等的,即多次执行相同的GET请求应该返回相同的结果,并且不应该对服务器产生副作用。
  2. POST:用于将数据提交到服务器以创建新的资源。POST请求可能会导致服务器状态的变化,因此它不是安全和幂等的。通常用于提交表单数据或在服务器上执行某些操作。
  3. PUT:用于在服务器上更新指定的资源。PUT方法是幂等的,即对同一资源的多个PUT请求具有相同的结果。通常用于更新整个资源。
  4. DELETE:用于删除服务器上的指定资源。DELETE方法是幂等的,即多次执行相同的DELETE请求应该具有相同的结果。
  5. PATCH:用于对资源进行局部更新。与PUT不同,PATCH方法只更新资源的部分内容。
  6. HEAD:类似于GET方法,但服务器只返回响应头部,不返回实际数据主体。用于检查资源是否存在以及获取资源的元数据,而不传输整个资源内容。
  7. OPTIONS:用于获取服务器支持的HTTP方法列表,以及服务器的通信选项。
  8. TRACE:用于对服务器执行一个“环回”诊断,返回服务器收到的请求的原始内容,用于测试和调试。
  9. CONNECT:用于建立与代理服务器的隧道,通常用于HTTPS请求。

1.4 http各个版本优缺点

  1. HTTP/1.0

    优点:

    • 简单:HTTP/1.0是第一个版本的HTTP协议,非常简单,易于实现和理解。
    • 兼容性:由于早期的版本,几乎所有的浏览器和服务器都支持HTTP/1.0。

    缺点:

    • 效率低:HTTP/1.0每次请求只能获取一个资源,需要频繁建立和关闭连接,导致效率较低。
    • 多次连接:每个资源都需要单独的连接,增加了延迟和资源浪费。
  2. HTTP/1.1

    优点:

    • 持久连接:引入了持久连接(也称为连接复用),可以在同一连接上发送多个请求,减少了建立和关闭连接的开销,提高了效率。
    • 分块传输:引入了分块传输编码,允许边下载边解析,提高了页面加载速度。
    • 虚拟主机:支持在同一个IP地址上运行多个域名,提高了服务器资源的利用率。
    • 缓存控制:引入了更多的缓存控制机制,提高了缓存的效率和准确性。

    缺点:

    • 队头阻塞:由于连接复用的引入,一个请求阻塞会影响后续的请求,导致队头阻塞问题。
  3. HTTP/2

    优点:

    • 多路复用:HTTP/2引入了多路复用,可以在同一连接上同时发送多个请求和响应,解决了HTTP/1.x中的队头阻塞问题,提高了并发性能。
    • 头部压缩:使用HPACK算法对请求和响应头部进行压缩,减少了数据传输的大小,降低了延迟。
    • 二进制传输:HTTP/2使用二进制格式传输数据,相较于HTTP/1.x的文本格式,更加高效。
    • 服务器推送:服务器可以主动推送客户端可能需要的资源,减少了客户端请求的次数。

    缺点:

    • 部署困难:由于引入了新的特性,部署和配置HTTP/2可能相对复杂。
  4. HTTP/3

    优点:

    • 基于UDP:HTTP/3使用QUIC协议作为底层传输,基于UDP而不是TCP。QUIC解决了TCP的队头阻塞问题,并提供更快的连接建立和断开速度。
    • 零延迟连接:QUIC支持0-RTT连接,允许客户端在之前已建立连接的情况下,进行快速的重新连接。
    • 适应性:HTTP/3通过支持快速的连接迁移,允许客户端从移动网络切换到Wi-Fi等网络环境,而不会中断连接。

    缺点:

    • 较新的协议:HTTP/3仍然处于较新的阶段,可能在部分浏览器和服务器上没有广泛支持。
    • 迁移复杂:由于HTTP/3使用UDP作为底层传输,与现有的基于TCP的基础设施不兼容,迁移可能需要更多工作。

2. http框架的设计与实现

2.1 http的分层设计

HTTP(Hypertext Transfer Protocol)采用了分层设计的架构,这种设计允许不同层次的功能独立进行开发和演进,使得协议更加灵活和易于扩展。HTTP的分层设计主要包含以下几个层次:

  1. 应用层(Application Layer) : 应用层是HTTP协议的最顶层,它定义了HTTP协议的高级语义和数据格式。在这一层,HTTP定义了客户端和服务器之间的交互方式、请求方法、状态码、头部字段、URL格式、缓存控制等。应用层使用上层应用的数据并负责对数据的处理和传输。
  2. 传输层(Transport Layer) : 传输层负责在网络上传输应用层的数据。HTTP最常用的传输层协议是TCP(Transmission Control Protocol)。TCP提供了可靠的、面向连接的数据传输,确保数据的可靠性和顺序性。但是,随着HTTP/3的引入,HTTP开始使用基于UDP的QUIC协议作为传输层,以获得更好的性能和低延迟。
  3. 网络层(Network Layer) : 网络层负责在计算机网络中进行数据的路由和转发。它将传输层的数据分割成数据包,并将它们发送到目标服务器。网络层使用IP(Internet Protocol)来定义数据在网络中的传输方式。
  4. 数据链路层(Data Link Layer) : 数据链路层负责将数据包从一个节点传输到另一个节点。在数据链路层,数据被分成帧,并通过网络硬件设备(如网卡)发送。此层通常与特定的物理设备和网络技术相关。

2.2 http框架的分层设计

image.png

以下是HTTP框架的典型分层设计:

  1. 路由层: 路由层是HTTP框架的入口点,负责处理传入的HTTP请求并将其分派到相应的处理程序。在这一层,定义了路由规则,将URL和HTTP方法与处理程序关联起来。路由层通常使用正则表达式或其他匹配规则来解析URL,并将请求转发给适当的处理程序。
  2. 控制器层: 控制器层是应用程序的中间层,负责处理业务逻辑。在MVC模式中,控制器层负责接收请求、处理请求数据、调用模型层进行数据处理,并最终将结果返回给视图层。控制器层通常包含各种处理器函数或方法,用于处理不同类型的HTTP请求。
  3. 模型层: 模型层负责处理数据和业务逻辑。它与数据库交互,执行数据查询、更新和存储操作。模型层还包含应用程序的业务逻辑,用于处理数据的验证、转换和处理。在一些框架中,模型层可能也包含ORM(对象关系映射)或其他数据持久化工具。
  4. 视图层: 视图层负责处理响应的展示和渲染。它接收控制器层传递的数据,并将其呈现为适当的响应格式,如HTML、JSON、XML等。视图层通常使用模板引擎来处理动态内容,将数据填充到预定义的模板中,生成最终的响应。
  5. 中间件层: 中间件层是一种横切关注点(Cross-cutting Concerns),负责在请求处理过程中添加通用功能,如日志记录、认证、授权、缓存等。中间件在路由层和控制器层之间进行操作,并可以在请求的前后对请求进行预处理和后处理。

3. http框架实战

3.1 性能优化

  • 网络库

1.存下全部 Header 2.减少系统调用次数 3.能够复用内存 4.能够多次读

  • 不同网络库对比
  • go netl 流式友好 小包性能高
  • netpoll 中大包性能高 时延低
  • header的处理
  • 1.通过 Header key 首字母快速筛除掉完全不可能的 key
  • 2.解析对应 value 到独立字段
  • 3.使用 byte slice 管理对应 header 存储,方便复用

4.基于前缀路由树的注册与查找的伪代码实现

下面示例中,我们使用Go语言实现了一个简单的前缀路由树,用于注册和查找字符串。通过Insert方法将字符串添加到树中,通过Search方法可以检查树中是否存在给定的字符串。

 // 定义前缀路由树的节点
 type TrieNode struct {
     Children map[rune]*TrieNode
     IsEndOfWord bool
 }
 ​
 // 定义前缀路由树
 type Trie struct {
     Root *TrieNode
 }
 ​
 // 初始化前缀路由树
 func NewTrie() *Trie {
     return &Trie{
         Root: &TrieNode{
             Children: make(map[rune]*TrieNode),
         },
     }
 }
 ​
 // 注册一个字符串到前缀路由树中
 func (t *Trie) Insert(word string) {
     node := t.Root
     for _, char := range word {
         if _, ok := node.Children[char]; !ok {
             node.Children[char] = &TrieNode{
                 Children: make(map[rune]*TrieNode),
             }
         }
         node = node.Children[char]
     }
     node.IsEndOfWord = true
 }
 ​
 // 查找前缀路由树中是否存在指定的字符串
 func (t *Trie) Search(word string) bool {
     node := t.Root
     for _, char := range word {
         if _, ok := node.Children[char]; !ok {
             return false
         }
         node = node.Children[char]
     }
     return node.IsEndOfWord
 }
 ​
 // 创建前缀路由树实例
 trie := NewTrie()
 ​
 // 注册一些字符串到前缀路由树中
 trie.Insert("apple")
 trie.Insert("banana")
 trie.Insert("orange")
 ​
 // 查找前缀路由树中是否存在指定的字符串
 fmt.Println(trie.Search("apple"))  // 输出 true
 fmt.Println(trie.Search("grape"))  // 输出 false
 ​