随着.net 5在11月的发布,如今是谈论网络栈中许多改进的好时机。这包括对HTTP、套接字、与网络相关的安全性和其余网络通讯的改进。在这篇文章中,我将重点介绍一些版本中更有影响力和更有趣的变化。ios
HTTPgit
更好的错误处理 github
自从.net 3.1发布以来,HTTP领域进行了许多改进和修复。当使用HttpClien时,最受关注的是添加如何区分超时和取消。最初,不得不使用自定义的CancellationToken区分超时和取消:算法
这样作,客户端仍然抛出TaskCanceledException(为了兼容),但内部异常是超时时的TimeoutException:编程
另外一个改进是将HttpStatusCode添加到HttpRequestException中。当响应上调用EnsureSuccessStatusCode时,新的StatusCode属性能够设置为空。而后,它能够在异常过滤器中使用:小程序
因为HttpClient中方法:GetStringAsync, GetByteArrayAsync和GetStreamAsync不返回HttpResponseMessage,它们本身调用EnsureSuccessStatusCode。这些调用的异常过滤以下所示:后端
因为新的构造函数是public的,因此能够手动建立带有状态码的HttpRequestException:api
一致的跨平台实现浏览器
最初,.NET Core中的HTTP栈依赖于平台相关的处理程序:安全
因为两个库之间的差别,几乎不可能实现跨平台的一致性。所以,在.net Core 2.1中,咱们引入了一个名为SocketsHttpHandler的托管HTTP实现。咱们将大部分工做转移到SocketsHttpHandler,随着咱们对它的可靠性愈来愈有信心,咱们决定彻底从System.Net.Http.dll中删除特定于平台的处理程序。在.net 5中,再也不可能使用切换回System.Net.Http。然而,WinHttpHandler仍然做为一个独立的NuGet包可用。任何使用它的代码都须要更改成引用System.Net.Http.WinHttpHandler的NuGet包:
并显式地向HttpClient构造函数传递WinHttpHandler实例:
SocketsHttpHandler扩展点
HttpClient是一个高级API,使用方便,但在某些状况下缺少灵活性。在更高级的场景中,须要更精细的控制。咱们试图弥合这些差距,并在SocketsHttpHandler中引入了两个扩展点——ConnectCallback和PlaintextStreamFilter。
ConnectCallback容许自定义建立新链接。每次打开一个新的TCP链接时都会调用它。回调可用于创建进程内传输、控制DNS解析、控制基础套接字的通用或特定于平台的选项,或者仅用于在新链接打开时通知。回调有如下注意事项:
当不提供回调时的默认实现等价于如下最小的、基于套接字的回调:
另外一个扩展点,PlaintextStreamFilter,容许在新打开的链接上插入一个自定义层。在链接彻底创建以后(包括用于安全链接的TLS握手),但在发送任何HTTP请求以前调用此回调。所以,可使用它来监听经过安全链接发送的纯文本数据。这个回调的通常准则是:
如何实现自定义流能够在文档中找到。自定义流最终应该将读写任务委托给所提供的流,但它能够拦截交换的数据。
一个很是小的没有自定义流的PlaintextStreamFilter示例以下:
建立新扩展点链接的时间轴为:
若是没有注册回调函数,这里就不会调用任何东西。这两个回调函数都是为了对SocketsHttpHandler中的链接进行高级控制。应该很是当心地执行和测试它们,由于它们可能会无心中致使性能和稳定性问题。
HttpClient.Send的同步API
虽然咱们建议使用异步网络API以得到更好的性能和可伸缩性,但咱们也认识到,在某些状况下,使用同步API是必要的,而且会同步阻塞等待HttpClient。SendAsync常常有可伸缩性问题,由于须要多个线程来完成一个操做。这种方法的其余缺陷,包括臭名昭著的UI线程死锁。
为了启用同步场景并避免这些问题,咱们添加了一个同步版本的HttpClient.Send,可是实现有一些注意事项:
咱们强烈建议尽量继续使用异步api。
HTTP / 2
版本选择
这个特性是从支持明文HTTP/2 (h2c)的请求演变而来的。明文通讯不只适用于本地调试或测试环境,还可能存在防火墙或反向代理后的基于HTTP/2的服务,这些服务不使用TLS。例如,gRPC服务使用HTTP/2做为传输协议,有些服务选择放弃加密。
直到.net 5,一个不受支持的应用程序开关必须被打开才能启用明文HTTP/2通讯,这可能会有问题,由于它不能启用每一个请求控制。若是没有交换机,每一个明文HTTP/2请求都会自动降级为HTTP/1.1。这是由于TLS扩展ALPN被用于与服务器协商最终的HTTP版本。没有TLS,所以没有ALPN,客户端不能肯定服务器将可以处理HTTP/2。所以,客户端避免了风险,并选择了广泛支持的HTTP/1.1。可是,在前面提到的后端服务和gRPC的状况下,可能事先就知道全部参与者均可以处理h2c,所以自动降级是不可取的。
当咱们设计版本选择时,咱们试图归纳原来的问题,并使API合理地“证实将来”。所以,咱们决定让用户控制如何处理版本的降级和升级。咱们引入了HttpVersionPolicy,这是一个新的enum,表示是否接受降级、升级,或者只接受准确的版本。用于手动建立并由HttpClient.SendAsync的发送。策略能够经过HttpRequestMessage.VersionPolicy直接设置到请求。对于GetAsync、PostAsync、DeleteAsync等在内部建立请求的调用,HttpClient实例属性HttpClient.DefaultVersionPolicy用于控制策略。
例如,要启用h2c场景,咱们能够这样作:
与HTTP/2的多个链接
HTTP/2容许多个并发请求一个TCP链接上的多路传输。根据HTTP/2规范,只应该向服务器打开一个TCP链接。这个建议对于浏览器很是有效,而且解决了HTTP/1打开每一个源的多个链接的问题。然而,这将最大并发请求数减小到设置帧中的值,一般能够设置为100。对于服务到服务的通讯,其中一个客户机向少许服务器发送很是多的请求,而且/或能够保持多个长期存在的请求,这一限制会显著影响吞吐量和性能。为了克服这个限制,咱们引入了向单个端点打开多个HTTP/2链接的能力。
默认状况下,多个HTTP/2链接是禁用的。要启用它们,将SocketsHttpHandler.EnableMultipleHttp2Connections设置为true。
多个并发请求的示例以下:
控制台将显示来自ConnectCallback关于建立新链接的多条消息。若是将EnableMultipleHttp2Connections注释掉,控制台将只显示一条消息。
可配置的PING
HTTP/2规范定义了PING帧,这是一种确保空闲链接保持活跃的机制。此特性对于长时间运行的空闲链接很是有用,不然这些空闲链接将被删除。这样的链接能够在gRPC场景中找到,好比流和长时间的远程过程调用。到目前为止,咱们只回复PING请求,从不发送。
在.net 5中,咱们已经实现了发送PING帧的可配置间隔、超时,以及是否老是或仅在活动请求时发送它们。默认值的配置是:
默认值KeepAlivePingDelay (Timeout.InfiniteTimeSpan)意味着该特性一般是关闭的,PING帧不会自动发送到服务器。客户端仍然会回复收到的PING帧,这是不能关闭的。为了启用自动PING, KeepAlivePingDelay必须更改,例如1分钟:
只有当与服务器没有主动通讯时才发送PING帧。每个来自服务器的传入帧都将重置延迟,只有在KeepAlivePingDelay没有接收到帧以后,才会发送一个PING帧。而后,服务器被给予KeepAlivePingTimeout应答时间间隔。若是没有,则认为链接丢失并被拆除。该算法会按期检查延迟和超时,但最多每秒钟检查一次。将KeepAlivePingDelay或KeepAlivePingTimeout设置为更小的值将致使异常。
例如,设置以下:
将致使1.875秒间隔,由于它是两个值的1/4,即min(KeepAlivePingDelay, KeepAlivePingTimeout)/4。在这种状况下,超时可能发生在发送PING帧后的7.5到9.5秒之间。注意,检查间隔的计算是一个实现细节,未来可能会更改。
HTTP / 3
HTTP/3及其底层传输层QUIC正处于标准化的最后阶段。QUIC是一种新的基于udp的传输,与基于TCP的链接相比,它提供了一些好处:
-TLS安全连接握手更快
-在单个链接上更可靠的多路复用多个请求,消除了当数据包被丢弃时线路阻塞问题。
-链接迁移使移动客户端网络之间的转换更加流畅,例如Wi-Fi到LTE再返回。
. net 5引入了对HTTP/3的实验性支持——目前还不建议在生产环境中使用该特性。在底层,咱们使用的是MsQuic库,它是一个开源的、跨平台的QUIC协议实现。如何使用QUIC启用HTTP/3的详细说明能够在System.Net.Experimental.MsQuic中找到。
这个设置告诉HttpClient咱们已经预先知道了服务器支持HTTP/3并请求它。若是不支持HTTP/3,则会抛出异常。另外一种选择是让服务器经过Alt-Svc报头发布HTTP/3。而后客户端能够将其用于后续请求。对于这个场景,请求不该该要求RequestVersionExact,由于它须要用较低的协议版本处理第一个请求。
更好的取消支持
基于Task的异步方法如今是异步编程的首选模式。它们在须要进行大量I/O操做的网络中特别有价值。Task模式使代码比原来的开始/结束“APM”模式更容易理解。基于Task的异步模式的一部分是使用CancellationToken来取消和超时。咱们一直在努力添加取消令牌,并将其正确地应用到各个地方。咱们仍然有遗漏重载的漏洞,但咱们已经在.net 5中填补了许多。
对于socket,咱们在SocketTaskExtensions中添加了重载——咱们想在.net 6中将它们放Socket类自己中。使用CancellationToken的新重载以下:
这些重载已经在HttpClient和TcpClient中使用,致使了TcpClient中的新的重载:
在HTTP命名空间中,咱们添加了HttpClient 和HttpContent 的重载。' HttpClient '被扩展为' Get(ByteArray|Stream|String)Async '重载:
HttpContent添加了序列化和读取的重载:
若是咱们如今设计HttpContent,使用全部重载,咱们将使CreateContentReadStreamAsync和SerializeToStreamAsync变为abstract 而不是virtual。问题是咱们试图不经过改变公共类的契约来破坏现有的代码。在.net Core 3.1下运行的内容应该继续在.net 5下运行,没有任何改变。添加abstract 方法违背了这一承诺,因此咱们不得不求助于virtual方法。自定义HttpContent实现应该重写它们,尽管它们只是virtual。全部HttpContent实现,好比byteraycontent、MultipartContent和StreamContent,都已经这样作了。
网络遥测
咱们已经意识到,用户关于监视.net Core应用程序的内部网络描述并很差。到目前为止,只能收集很是详细且不一致的日志消息,侦听它们对性能有影响。对于.net 5,咱们设计并实现了一套新的遥测事件和计数器。这些事件和计数器是在考虑持续监视的状况下建立的,所以它们不像内部日志那样占用大量资源。然而,它们并非彻底没有代价的,监听会消耗一些(尽管不多)CPU周期。
咱们正在公开这些新的遥测事件和计数器,它们将供.net用户使用。咱们计划在将来支持它们,对它们的任何更改都将被视为突破性的更改。
遥测事件和计数器都基于EventSource。它们能够经过EventListener在进程内使用,也能够经过EventPipe经过dotnet-trace和dotnet-counters命令行工具在进程外使用。
自定义遥测事件
一种自定义遥测事件的方法是经过EventListener在进程中编程:
这个小程序将产生以下的控制台日志:
命名为*Start和*Stop的事件使用相同的ActivityId触发。这些事件具备特殊的意义并自动关联。它容许像PerfView这样的监视工具计算操做所消耗的时间,或将其余事件连接到父事件。
另外一种方法是经过dotnet-trace进程以外:
计数器
计数器能够经过EventListener在进程中以编程方式使用:
控制台日志是这样的:
或进程外部的启动计数器:
这将启动计数器监视,用实际值覆盖终端窗口,看起来相似:
. net中的安全层依赖于底层操做系统及其功能。
-对于基于Linux的系统,咱们使用OpenSSL,它从1.1.1版本起就支持TLS 1.3。
-对于Windows 10, TLS 1.3是可用的版本1903,但只用于测试目的,而不是生产。
此外,它是可选的,必须在注册表中启用。所以,TLS 1.3在以前的.net Core版本不能在Windows上工做。
这在内部预览版中有所改变,其中TLS 1.3是默认开启的,能够经过新的API使用。咱们针对新的API调整了Windows上的SslStream实现,并在.net 5的Windows内部预览版本中对其进行了测试。
咱们还追求在.net 5的SSL测试中得到A级。为了实现这一点,咱们必须对Linux上的SslStream引入一个破坏性的更改,咱们如今设置了一个被认为是强大的默认密码套件的自觉得是的列表:
最后指出
本文并非咱们所作的全部更改的完整列表。若是你发现任何错误,请绝不犹豫地联系咱们,你能够在dotnet/ncl别名下找到咱们。
欢迎关注个人公众号,若是你有喜欢的外文技术文章,能够经过公众号留言推荐给我。
原文连接:https://devblogs.microsoft.com/dotnet/net-5-new-networking-improvements/