TCPClient 的性能非常慢
Terribly slow performance of TCPClient
出于学习目的(我想牢牢掌握 HTTP 内部结构)我正在编写简单的 HTTP 服务器,它将根据 rfc7230-7235 解析请求并将其代理到我的简单后端服务器。
我不希望击败任何现有解决方案,但显然 TCPClient
由于某种原因工作速度非常慢。直接调用我的后端在更糟糕的情况下只需要 20 毫秒,而使用我的简单服务器调用它至少需要 200 毫秒,这太糟糕了。
撇开解析不谈,这对响应时间的影响几乎微不足道,这是最小的代码,可以接收响应并将其原样发送到后端:
public static async Task Main()
{
Logger.Info("Start listener.");
var listener = new TcpListener(IPEndPoint.Parse("0.0.0.0:5000"));
listener.Start();
while (true)
{
var client = await listener.AcceptTcpClientAsync();
using var c_stream = client.GetStream();
// read request
using var ms = new MemoryStream();
int i;
var buffer = new byte[1024];
while ((i = await c_stream.ReadAsync(buffer, 0, 1024)) != 0)
{
await ms.WriteAsync(buffer, 0, i);
if (i < 1024) break;
}
// write whole request as-is to backend
using var backend = new TcpClient();
await backend.ConnectAsync(IPEndPoint.Parse("172.21.215.119:3000"));
var b_stream = backend.GetStream();
ms.Position = 0;
ms.WriteTo(b_stream);
await b_stream.FlushAsync();
// read output from backend to memory
ms.Position = 0;
while ((i = await b_stream.ReadAsync(buffer, 0, 1024)) != 0)
{
await ms.WriteAsync(buffer, 0, i);
if (i < 1024) break;
}
// send back to fuckin client
ms.Position = 0;
ms.WriteTo(c_stream);
await c_stream.FlushAsync();
}
}
我不知道这是否重要,但我的环境看起来像这样:
- 我正在 windows 10 台装有 WSL 2 的机器上工作。
- 后端在 WSL 上设置 (ubuntu)。这只是nestjs上的宠物店。
- 后端调用 mongodb,这是在 docker 环境(也是 WSL)中设置的。
在我的最小代码示例中,每个请求需要 200-250 毫秒,至少在我的机器上是这样。它与我的实际代码的工作方式没有太大区别。最大的区别可能是,我正在为每个请求发送垃圾邮件任务,并且我有很多与 RFC 要求相关的验证。
如果有一些关于如何正确使用 TCPClient(或必要时使用 Sockets)的好资源,我会很乐意接受。
好吧,看来答案很……打扰了……
我通过将对 http://localhost:port
的调用更改为 http://127.0.0.1:port
来解决这个问题。
现在即使是我在这个例子中的愚蠢代理实现,最少的代码也需要几毫秒才能完成 运行。
windows 上的本地主机解析似乎存在某种问题,老实说这超出了我的范围。显然这不仅是我的问题,reddit 上的一些用户 (post) 帮助了我,在尝试 运行 类似于我的代码时遇到了同样的问题。
出于学习目的(我想牢牢掌握 HTTP 内部结构)我正在编写简单的 HTTP 服务器,它将根据 rfc7230-7235 解析请求并将其代理到我的简单后端服务器。
我不希望击败任何现有解决方案,但显然 TCPClient
由于某种原因工作速度非常慢。直接调用我的后端在更糟糕的情况下只需要 20 毫秒,而使用我的简单服务器调用它至少需要 200 毫秒,这太糟糕了。
撇开解析不谈,这对响应时间的影响几乎微不足道,这是最小的代码,可以接收响应并将其原样发送到后端:
public static async Task Main()
{
Logger.Info("Start listener.");
var listener = new TcpListener(IPEndPoint.Parse("0.0.0.0:5000"));
listener.Start();
while (true)
{
var client = await listener.AcceptTcpClientAsync();
using var c_stream = client.GetStream();
// read request
using var ms = new MemoryStream();
int i;
var buffer = new byte[1024];
while ((i = await c_stream.ReadAsync(buffer, 0, 1024)) != 0)
{
await ms.WriteAsync(buffer, 0, i);
if (i < 1024) break;
}
// write whole request as-is to backend
using var backend = new TcpClient();
await backend.ConnectAsync(IPEndPoint.Parse("172.21.215.119:3000"));
var b_stream = backend.GetStream();
ms.Position = 0;
ms.WriteTo(b_stream);
await b_stream.FlushAsync();
// read output from backend to memory
ms.Position = 0;
while ((i = await b_stream.ReadAsync(buffer, 0, 1024)) != 0)
{
await ms.WriteAsync(buffer, 0, i);
if (i < 1024) break;
}
// send back to fuckin client
ms.Position = 0;
ms.WriteTo(c_stream);
await c_stream.FlushAsync();
}
}
我不知道这是否重要,但我的环境看起来像这样:
- 我正在 windows 10 台装有 WSL 2 的机器上工作。
- 后端在 WSL 上设置 (ubuntu)。这只是nestjs上的宠物店。
- 后端调用 mongodb,这是在 docker 环境(也是 WSL)中设置的。
在我的最小代码示例中,每个请求需要 200-250 毫秒,至少在我的机器上是这样。它与我的实际代码的工作方式没有太大区别。最大的区别可能是,我正在为每个请求发送垃圾邮件任务,并且我有很多与 RFC 要求相关的验证。
如果有一些关于如何正确使用 TCPClient(或必要时使用 Sockets)的好资源,我会很乐意接受。
好吧,看来答案很……打扰了……
我通过将对 http://localhost:port
的调用更改为 http://127.0.0.1:port
来解决这个问题。
现在即使是我在这个例子中的愚蠢代理实现,最少的代码也需要几毫秒才能完成 运行。
windows 上的本地主机解析似乎存在某种问题,老实说这超出了我的范围。显然这不仅是我的问题,reddit 上的一些用户 (post) 帮助了我,在尝试 运行 类似于我的代码时遇到了同样的问题。