{ "cells": [ { "cell_type": "markdown", "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "source": [ "# HttpClient 概述" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 作用\n", "HttpClient是一个用于发送HTTP请求和接收HTTP响应的类。它提供了一种现代化、灵活和强大的方式来与Web服务进行通信。HttpClient类位于System.Net.Http命名空间下,可以通过NuGet包管理器进行安装。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## 整体理解" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![图](./Assets/概要图.svg) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "HttpClient是应用程序进程中,封装调用远程请求的类库。整个远程的执行,是由进程向操作系统申请,经由网卡、路由器等网络设备转换、传输到一台Web服务器,然后返回的过程, HttpClient只是其中一个很小的环节。\n", "\n", "在这个基础上,理解初代HttpClient问题(网络套接字用完 DNS问题等)、使用原则、请求管道、工厂模式、类型化客户端、Polly等项时会很轻松。比如:即便使用Using包括了 HttpClient,也会有套接字耗尽的问题,是因为using只能释放进程中的对象,但进程管理不了系统及网卡(这是在进程之上的),更不用说路由和互联网及被调用方的服务器了。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 前世今生" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### WebRequest\n", "这是.NET创建者最初开发用于使用HTTP请求的标准类。\n", "\n", "使用HttpWebRequest可以让开发者控制请求/响应流程的各个方面,如 timeouts, cookies, headers, protocols。另一个好处是HttpWebRequest类不会阻塞UI线程。例如,当您从响应很慢的API服务器下载大文件时,您的应用程序的UI不会停止响应。\n", "\n", "HttpWebRequest通常和WebResponse一起使用,一个发送请求,一个获取数据。HttpWebRquest更为底层一些,能够对整个访问过程有个直观的认识,但同时也更加复杂一些。\n", "\n", "### WebClient\n", "WebClient是一种更高级别的抽象,是HttpWebRequest为了简化最常见任务而创建的,使用过程中你会发现他缺少基本的header,timeoust的设置,不过这些可以通过继承httpwebrequest来实现。使用WebClient可能比HttpWebRequest直接使用更慢(大约几毫秒)。但这种“低效率”带来了巨大的好处:它需要更少的代码和隐藏了细节处理,更容易使用,并且在使用它时你不太可能犯错误。\n", "\n", "### HttpClient\n", "HttpClient提供强大的功能,并提供了异步支持,可以轻松配合async await 实现异步请求等。\n", "\n", "随着更新发展,已经解决了前期问题(套接字耗尽、DNS、不灵活等),并发展了完整、完善的体系(基于Task的异步、连接池、类型化的客户端、工厂模式、Polly等)。\n", "\n", "是本项目的学习目标。" ] }, { "cell_type": "markdown", "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" }, "vscode": { "languageId": "polyglot-notebook" } }, "source": [ "## HttpClient架构图\n", "\n", "### .NetFramework 4及4.5 架构\n", "HttpClient最初是作为NuGet包提供的,该包可以选择包含在.NET Framework 4.0项目中。在.NET Framework 4.5中,它作为BCL(基本类库)的一部分在框中提供。它建立在预先存在的HttpWebRequest实现之上。在.NET Framework中,ServicePoint API可用于控制和管理HTTP连接,包括通过为端点配置ConnectionLeaseTimeout来设置连接寿命。\n", "\n", "![.NetFramework](./Assets/架构.001.png)\n", "\n", "### .NET Core 1.0及1.1 架构\n", ".NET Core 1.0最初于2016年6月发布。与.NET Framework中可用的版本相比,此第一个版本的API接口要小得多,主要用于构建ASP.NET Core Web应用程序。由于.NET Core 1.0是HttpClient,因此提供了API。但是,不包括用于HttpWebRequest和ServicePoint的API。.NET Core 1.0中的HttpClient直接建立在使用非托管代码的OS平台API之上,Windows API使用WinHTTP,Linux和Mac使用LibCurl。\n", "\n", "值得注意的是:到2016年8月,很快就注意到,重新使用HttpClient实例以防止套接字耗尽的建议有一个相当麻烦的副作用:DNS\n", "\n", "![.NetFramework](./Assets/架构.002.png)\n", "\n", "### .NET Core 2.0 架构\n", "在.NET Core 2.0中,添加了HttpWebRequest以支持.NET Standard 2.0。它位于HttpClient实现的顶层,这与.NET Framework 4.5+中的工作原理相反。还添加了ServicePoint,尽管它的许多API接口要么要么会抛出未实现的异常,要么根本就没有实现。\n", "\n", "![.NetFramework](./Assets/架构.003.png)\n", "\n", "### .NET CORE 2.1及后续 架构\n", "这种有问题的行为导致团队不同团队进行了两项工作。\n", "\n", "ASP.NET团队开始研究**Microsoft.Extensions.Http包,该包的主要功能是IHttpClientFactory**。这个针对HttpClient实例自用的工厂还包括基础HttpMessageHandler链的生命周期管理。如果您想了解有关此功能的更多信息,可以查看我的系列博客文章,我将在此介绍。\n", "\n", "IHttpClientFactory功能是作为ASP.NET Core 2.1的一部分发布的,对于许多人来说,这是一个很好的折衷方案,它解决了连接重用以及生命周期管理的问题。\n", "\n", "在同一时间范围内,.NET团队正在研究自己的解决方案。该团队也在.NET Core 2.1中发布,在HttpClient的处理程序链的核心引入了一个新的**SocketsHttpHandler**。该处理程序直接建立在Socket API之上,并在托管代码中实现HTTP。这项工作的一部分包括连接池系统以及为这些连接设置最大生存期的能力。\n", "\n", "![.NetFramework](./Assets/架构.004.png)\n", "\n", "说明:虽然默认情况下从.NET Core 2.1启用了SocketsHttpHandler,但实现仅限于HTTP / 1.1通信。那些需要HTTP / 2的用户必须禁用该功能并使用较旧的处理程序链,该处理程序链像以前一样依赖非托管代码,并且不包括连接池。\n", "\n", "幸运的是,.NET Core 3.0中已消除了此限制,并且现在提供了HTTP/2支持。这应该使用基于适合所有对象的SocketsHttpHandler链的HttpClient。\n", "\n", "结语:从.Net core 2.1开始,架构逐步稳定、完善。.Net 5、6、7、8中,逐渐发展出一整套完整机制,包括但不限于 连接池、工厂模式、Polly等。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 后续\n", "\n", "后续将从基础使用到高级使用,一步步展开,一步步学习。" ] } ], "metadata": { "kernelspec": { "display_name": ".NET (C#)", "language": "C#", "name": ".net-csharp" }, "language_info": { "name": "python" }, "polyglot_notebook": { "kernelInfo": { "defaultKernelName": "csharp", "items": [ { "aliases": [], "name": "csharp" } ] } } }, "nbformat": 4, "nbformat_minor": 2 }