Golang Blazing Fast Unit Tests - Fiber/fasthttp/http 内部结构和优化 HTTP 服务器测试
Golang Blazing Fast Unit Tests - Fiber/fasthttp/http 内部结构和优化 HTTP 服务器测试
您好,在本文中,我将告诉您如何在我们使用 Go 开发的项目中提高 HTTP 服务器的测试性能。
最近,当我为我们正在开发的应用程序开发新功能时,我意识到我可以在等待单元测试完成的同时通勤喝咖啡。并且不可能并行运行测试。如果我们想以当前的形式并行运行它,这个测试周期可能需要更长的时间。
现在,让我们简要看一下内容,然后继续我们主题的细节。
内容
- 转到 HTTP 内部
— http.ListenAndServe
— net.Conn & net.Listener
— http.Client 拨号功能
— net.Pipe
在不同的包中测试 - 纤维
— app.Test 内部结构
— app.ServeConn - 快速http
— fasthttputil.NewInmemoryListener - 转到 HTTP
— httptest.NewUnstartedServer
— 模拟 InMemListener 示例 - 我们的测试性能改进
- 结论
转到 HTTP 内部
让我们看看当我们想用 Go 语言运行一个简单的 HTTP 服务器时,我们需要做些什么。
http.ListenAndServe
当我们想要运行使用 Go 标准库编写的服务器时,我们使用 聆听并服务
方法。
让我们来看看详细信息 聆听并服务
方法:
现在让我们来看看细节 server.ListenAndServe()
方法:
最后,我们来看看细节 srv.Serve(ln)
方法。我已经删除了一些代码,所以它不会太长。
这里我们要注意的部分是 l.接受
方法。
到目前为止我分享的代码中完成的过程摘要实际上是:
- http.ListenAndServe 方法在后台创建服务器并运行服务器 ListenAndServe 方法
- 服务器创建一个 net.Listener 对象,该对象接受来自指定端口的请求
net.Listen("tcp", addr)
方法调用并运行 Serve 方法。 - Serve 方法作用于
网康
表示新传入连接的对象,该对象的 Accept() 方法网络监听器
它作为参数接收的对象。
到目前为止,我们已经看到了创建基本形式的 API 的过程。
现在,我可以听到你慢慢地说,“好吧,先生,他们在现实生活中会做什么?”
别担心,我们很快就会看到如何使用这些信息来提高我们的测试性能。
net.Listen & net.Conn
现在让我们仔细看看 HTTP 包中包含的这两种基本类型。
我们看到这两种其实是 接口 .
嗯,这是什么意思?我们可以将我们实现这些接口作为参数的特殊结构提供给期望这些接口类型作为参数的方法。这对我们来说很重要。
http.Client拨号功能
现在让我们看看如何使用 http.Client 对象,它允许我们使用 Go 发出 HTTP 请求。
使用 Go HTTP 包发送请求有两种方法。
- 第一种方法是使用 http.Get http.Post 直接方法
- 第二种方法是使用 http.Client 对象
resp, _ := http.Get("http://localhost:8080/test")
其实第一种方法 http.Get ,也在后台使用 http.Client 对象。
让我们简单看一下:
换句话说,这两种方法在后台做同样的事情。
现在让我们看看我们在创建时可以自定义的字段 http.Client 目的。
http.Client 提供所有连接建立过程 往返 界面。它使用 运输 对象本身作为 RoundTripper 实现。
如果需要,我们可以在创建 http.Client 时自己创建这个 Transport 对象。
我们创建的 Transport 对象有一个方法叫做 拨号上下文 .该方法是在后台启用连接打开的方法,它返回一个类型的值 网康 作为变量。
稍后,我们将结合这些信息和我们刚刚看到的 server.Serve 方法正在使用 net.Listener 变量的 accept 方法侦听与 net.Conn 的连接的信息,该变量作为参数。
碎片一点一点地落到适当的位置,不是吗?
网管
最后,我想提一下另一种方法: 净管() .它返回两个 网康 类型变量作为返回值。
这些变量中的一个可以用作写入点,另一个用作读取点。那么这是什么意思?我们如何使用它?
如果你还记得, 听() 的方法 听众 对象返回变量 网康 .这 拨号() HTTP客户端的方法 运输 对象也返回变量 网康 .
从这里开始,当我们给连接返回的一端 净管() 的方法 拨号 我们的 HTTP 客户端的方法和另一端的 听 我们的方法 听众 对象,我们使用 HTTP 客户端发出的请求将创建侦听器所期望的连接。
在不同的包中测试
现在,让我们使用 Go 语言中常见的几个不同的 Web 应用程序库创建一个 API,并为它们编写测试。
首先,我们来看看 纤维 库,我们经常在我们的项目中使用它并且我为它做出了贡献。
写一个测试 纤维 非常简单,因为它包含一个直接的 Test 方法。
现在让我们看一个样本 纤维 应用程序及其测试方式:
这里我们需要注意线 app.Test(r, -1) .我们在这里发送我们的 HTTP 请求,以便我们可以测试它。
那么这个 HTTP 请求是如何在没有调用的情况下进行的 app.Listen(":8080")
我们通常会做什么来运行 Fiber 应用程序并让它在端口上侦听?
让我们来看看背后是什么 应用程序.Test() 方法(您可以访问所有代码 这里 ):
当我们检查代码时,我们看到 Fiber 实际上通过以下方式创建了自己的连接对象 conn := 新的(testConn)
为了测试应用程序,正如我们在 net.Pipe() 部分中解释的那样。它将 HTTP 请求写入此 conn 对象,并将此 conn 对象发送到服务器,并带有 app.server.ServeConn(conn)
称呼。
所以当我们说 app.Listen(":8080")
通常在 Fiber 应用程序中?让我们快速看一下:
在这里,我们看到 Fiber 净听()
在自身内部进行操作,并将它创建的侦听器对象提供给服务器,方法是说 app.server.Serve(ln)
.服务器侦听来自此侦听器对象的连接并将其作为参数传递给 服务连接() 方法。
换句话说,它实现了一种方法,将我们在开始时解释的 Listener 和 net.Conn 对象结合在一起,允许我们在不物理监听端口的情况下对其进行测试。
[
GitHub - gofiber/fiber: ⚡️ Express 启发的用 Go 编写的 web 框架
⚡️ 用 Go 编写的 Express 启发式 Web 框架。通过在…上创建一个帐户,为 gofiber/fiber 开发做出贡献
github.com
](https://github.com/gofiber/fiber)
快速http
我们说 Fiber 正在使用 快速http 包在后台。如果需要,我们可以直接使用 快速http 包裹。
现在让我们看看如何测试我们开发的应用程序 快速http .
在上面的代码中,我们创建了一个简单的服务器并定义了一个处理程序,当一个 得到 已向 /测试 端点,我们返回了 “好的” 回复。
在这里,与前面的代码不同,我们看到了一个类似的定义 ln := fasthttputil.NewInmemoryListener() .
接下来,我们定义一个 http.Client 并给出 拨号 我们创建的 ln 变量的方法 每日上下文 的方法 运输 目的。
那么这是否让你想起了什么?是的,我们在上面提到,我们可以给 网络监听器 反对我们的 http.Client 变量并在内存中测试它。 Fasthttp 在后台使用实用程序包正是采用这种方法。
对代码细节感兴趣的可以阅读 这里 .
[
fasthttp,Go的快速HTTP包,下载fasthttp的源码_GitHub_帮酷为高性能而调整。零记忆…
Go 的快速 HTTP 包。为高性能而调整。热路径中的零内存分配。比……快 10 倍
github.com
](https://github.com/valyala/fasthttp)
转到 HTTP
现在,让我们为使用 Go 自己的包开发的 API 编写一个测试,而不使用任何外部库。
假设我们开发了一个类似上面的 API。那么我们如何测试呢?
我们也可以使用我们在上面学到的技术来编写测试。 Go 语言有一个包叫做 httptest 其中包含一些实用方法。在这个包中,一个名为 新未启动服务器() 返回我们一个未启动的测试服务器对象。
让我们看一下源代码:
当我们创建一个服务器时 httptest.NewUnstartedServer() 方法,在后台创建一个监听器,监听随机端口 newLocalListener() 内部方法。
当我们说开始时,它会在 goroutine 中侦听来自侦听器的连接,其中 s.goServe() 方法。
当我们看到 Listener 时会想到什么?和 Fasthttp 一样,如果我们创建一个 InMemoryListener 并将其提供给我们的服务器,我们可以在不监听物理端口的情况下执行我们的测试。
在上面的代码中,我们通过实际监听测试中名为 TestHttpServer 的物理端口来运行我们的服务器。我把 时间.睡眠 等待服务器启动。使用 srv.Listener.Addr().String() 方法,我们可以获取我们需要请求的地址(包括操作系统随机分配的端口)。
在测试中称为 TestHttpServerInMemory ,我们通过我们创建的内存中的侦听器执行 http 服务器-客户端通信,而无需侦听物理端口。
在这里我使用了 fasthttputil.NewInMemoryListener 方便的方法。如果我们愿意,我们可以通过创建一个结构并实现监听器接口来做同样的事情。
让我们快速浏览一下:
正如我们在上面看到的,我们可以通过自己实现 Listener 接口并使用 净管() 方法。我给我们的连接对象的原因 InMemListener 结构上的通道是服务器调用 接受() 循环中的方法并等待新的连接。因此,只有当我们给我们的 Listener 对象一个连接时,服务器端才会收到一个新的连接。
那么为什么 Go 默认不提供这种内存服务器方法呢?
事实上,它有一个及时的问题,也有一些人在处理它,但过程还没有最终确定。
[
net/http/httptest: 可选更快的测试服务器 · 问题 #14200 · golang/go
您目前无法执行该操作。您使用另一个选项卡或窗口登录。您在另一个选项卡中退出或...
github.com
](https://github.com/golang/go/issues/14200)
我们的测试性能改进
现在让我们看看我们如何使用我上面描述的方法提高我们自己的测试性能。
我们开发了我们的应用程序 快速http 这个应用程序是作为一个基本的 反向代理 .我们还使用了一些由 快速http 用于反向代理操作。
对于我们的测试,我们需要一个反向代理(我们的应用程序)和一个假 API 来表示我们在后台重定向的服务器。
我们用了 httptest.NewUnstartedServer 为此,我们使用了 fasthttptest.NewInmemoryListener .
以前,我们实际上是在一个端口上侦听以在我们的测试中运行测试服务器,这大大减慢了我们的测试过程。当我从 CI 管道中查看时,我们的测试过程大约需要 10 秒。
作为我们开发的结果,在使用内存中侦听器方法后,我们所有的测试都在约 0.3 秒内完成。
在下图中,我们可以看到所有测试和总正常运行时间。
结论
- 当我们想要测试一个 HTTP 服务器时,我们可以更快地执行我们的测试,而无需监听物理端口。
- 使用这种技术,我们的测试运行得更快
- 我们可以使用 net.Pipe()、net.Listener、net.Conn 模拟内存中的网络操作
- 在使用 Fiber 测试我们的应用程序时,我们可以使用 app.Test() 方法。
- 我们可以使用 fasthttputil.InMemoryListener() 创建内存监听器
我希望这对您来说是一篇有趣且内容丰富的文章。下一篇文章见:)
让我们连接 :
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下