【译】.NET 6 网络改进

原文 | Máňa Píchová

翻译 | 郑子铭

对于 .NET 的每个新版本,我们都希望发布一篇博客文章,重点介绍网络的一些变化和改进。在这篇文章中,我很高兴谈论 .NET 6 中的变化。

这篇文章的上一个版本是 .NET 5 网络改进

HTTP

HTTP/2 窗口缩放

随着 HTTP/2 和 gRPC 的兴起,我们的客户发现 SocketsHttpHandler 的 HTTP/2 下载速度在连接到具有显着网络延迟的地理位置较远的服务器时无法与其他实现相提并论。在具有高带宽延迟产品的链路上,与其他能够利用链路物理带宽的实现相比,一些用户报告了 5 到 10 倍的差异。举个例子:在我们的一个基准测试中,curl 能够达到特定跨大西洋链路的最大 10 Mbit/s 速率,而 SocketsHttpHandler 的速度最高为 2.5 Mbit/s。除其他外,这严重影响了 gRPC 流式处理方案。

问题的根本原因是固定大小的 HTTP/2 接收窗口,当以高延迟接收 WINDOW_UPDATE 帧时,它的 64KB 大小太小而无法保持网络繁忙,这意味着 HTTP/2 自己的流量控制机制正在停止网络链接。

我们考虑了“廉价”选项来解决这个问题,例如定义一个固定大小的大窗口——这可能会导致不必要的高内存占用——或者要求用户根据经验观察手动配置接收窗口。这些似乎都不令人满意,因此我们决定实现一种类似于 TCP 或 QUIC 中的自动窗口大小调整算法 (dotnet/runtime#54755)。

结果证明效果很好,将下载速度提升到接近其理论最大值。但是,由于 HTTP/2 PING 帧用于确定 HTTP/2 连接的往返时间,因此我们必须非常小心,以免触发服务器的 PING 泛洪保护机制。我们实现了一个算法,该算法应该可以很好地与 gRPC 和现有的 HTTP 服务器一起工作,但我们想确保我们有一个逃生路径,以防出现问题。可以通过将 System.Net.SocketsHttpHandler.Http2FlowControl.DisableDynamicWindowSizing AppContext 开关设置为 true 来关闭动态窗口大小以及相应的 PING 帧。如果这变得有必要,仍然可以通过为 SocketsHttpHandler.InitialHttp2StreamWindowSize 分配更高的值来解决吞吐量问题。

HTTP/3 和 QUIC

在 .NET 5 中,我们发布了 QUIC 和 HTTP/3 的实验性实现。它仅限于 Windows 的 Insider 版本,并且有相当多的仪式让它工作。

在 .NET 6 中,我们大大简化了设置。

  • 在 Windows 上,我们将 MsQuic 库作为运行时的一部分提供,因此无需下载或引用任何外部内容。唯一的限制是需要 Windows 11 或 Windows Server 2022。这是因为 TLS 1.3 对 SChannel 中的 QUIC 的支持在早期的 Windows 版本中不可用。
  • 在 Linux 上,我们将 MsQuic 作为标准 Linux 包 libmsquic(deb 和 rpm)发布在 Microsoft Package Repository 中。在 Linux 上不将 MsQuic 与 runtime 捆绑在一起的原因是,我们将 libmsquic 与 QuicTLS 一起发布,QuicTLS 是 OpenSSL 的一个分支,提供了必要的 TLS API。由于我们将 QuicTLS 与 MsQuic 捆绑在一起,我们需要能够在正常的 .NET 发布计划之外进行安全补丁。

我们还大大提高了稳定性并实现了许多缺失的功能,在 .NET 6 里程碑中解决了大约 90 个问题

HTTP/3 使用 QUIC 而不是 TCP 作为其传输层。我们的 QUIC 协议的 .NET 实现是在 System.Net.Quic 库中的 MsQuic 之上构建的托管层。 QUIC 是一种通用协议,可用于多种场景,不仅仅是 HTTP/3,而且是新的,最近才在 RFC 9000 中获得批准。我们没有足够的信心认为当前的 API 形式能够经受住时间,并且适合其他协议使用,因此我们决定在此版本中将其保密。因此,.NET 6 包含 QUIC 协议实现,但没有公开它。它仅在内部用于 HttpClient 和 Kestrel 服务器中的 HTTP/3。

尽管在此版本中为消除错误付出了很多努力,但我们仍然认为 HTTP/3 的质量还没有完全为生产做好准备。由于任何 HTTP 请求都可能通过 Alt-Svc 标头无意中升级到 HTTP/3 并开始失败,因此我们选择在此版本中默认禁用 HTTP/3 功能。在 HttpClient 中,它隐藏在 System.Net.SocketsHttpHandler.Http3Support AppContext 开关后面。

我们之前的文章中已经描述了如何设置所有内容的所有细节:HttpClient 和 Kestrel。在 Linux 上,获取 libmsquic 包,在 Windows 上,确保操作系统版本至少为 10.0.20145.1000。然后,您只需要启用 HTTP/3 支持并将 HttpClient 设置为使用 HTTP/3:

using System.Net;

// Set this switch programmatically or in csproj:
// <RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
AppContext.SetSwitch("System.Net.SocketsHttpHandler.Http3Support", true);

// Set up the client to request HTTP/3.
var client = new HttpClient()
{
    DefaultRequestVersion = HttpVersion.Version30,
    DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher,
};
var resp = await client.GetAsync("https://<http3 endpoint>");

// Print the response version.
Console.WriteLine($"status: {resp.StatusCode}, version: {resp.Version}");

我们鼓励您尝试 HTTP/3!如果您遇到任何问题,请在 dotnet/runtime 中提出问题。

HTTP 重试逻辑

.NET 6 将 HTTP 请求重试逻辑更改为基于固定重试计数限制(请参阅 dotnet/runtime#48758)。

以前,.NET 5 不允许在“新”连接(未用于先前请求的连接)上发生连接失败时请求重试。我们这样做主要是为了确保重试逻辑不会陷入无限循环。这对于 HTTP/2 连接来说不是最理想的并且特别有问题(请参阅 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值