From 8e6c29bb65364b8a103f0e200247d0413f74f08e Mon Sep 17 00:00:00 2001 From: wanggaofeng <15601716045@163.com> Date: Wed, 24 Jul 2024 18:18:12 +0800 Subject: [PATCH] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Docs/1.3.0.基础使用.管理客户端.ipynb | 315 +++++++++++++++--- ... => 1.4.6.高级使用.工厂模式.ipynb} | 0 2 files changed, 262 insertions(+), 53 deletions(-) rename Docs/{1.4.6..高级使用.工厂模式.ipynb => 1.4.6.高级使用.工厂模式.ipynb} (100%) diff --git a/Docs/1.3.0.基础使用.管理客户端.ipynb b/Docs/1.3.0.基础使用.管理客户端.ipynb index 27397ec..58fca0f 100644 --- a/Docs/1.3.0.基础使用.管理客户端.ipynb +++ b/Docs/1.3.0.基础使用.管理客户端.ipynb @@ -25,7 +25,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" @@ -37,40 +37,7 @@ "languageId": "polyglot-notebook" } }, - "outputs": [ - { - "data": { - "text/html": [ - "
Installed Packages
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "启动WebApi项目\r\n" - ] - }, - { - "data": { - "text/plain": [ - "c:\\Users\\ruyu\\Desktop\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "程序[c:\\Users\\ruyu\\Desktop\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\r\n" - ] - } - ], + "outputs": [], "source": [ "//全局设置,行运行一次,为后续准备\n", "#r \"nuget:System.Net.Http.Json\"\n", @@ -117,17 +84,17 @@ "\n", "因为HttpClient刚推出时不成熟及微软官方文档的示例代码是这种用法,再加上这种是最简单方便的使用方法,就造成很多人使用这种用法。\n", "这种方法有如下缺点:\n", - "+ 1. 每次使用都实例化,造成性能开销大、容易内存泄露;\n", - "+ 2. 并发量大、请求频繁时:网络端口会被耗尽 `Using包HttpClient,也只是在应用进程中释放了HttpClient实例,但http请求/响应是跨操作系统和网络的,而系统及网络问题在进程之上,不是进程所能处理的。`\n", + " 1. 每次使用都实例化,造成性能开销大、容易内存泄露;\n", + " 2. 并发量大、请求频繁时:网络端口会被耗尽 `Using包HttpClient,也只是在应用进程中释放了HttpClient实例,但http请求/响应是跨操作系统和网络的,而系统及网络问题在进程之上,不是进程所能处理的。`\n", " \n", "优点:\n", - "+ 1. 使用简单,好学易用;\n", - "+ 2. 并发量小且请求不频繁时,问题不大;" + " 1. 使用简单,好学易用;\n", + " 2. 并发量小且请求不频繁时,问题不大;" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" @@ -139,26 +106,18 @@ "languageId": "polyglot-notebook" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "第 10 次/ 共 10 次请求,成功!" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "/*\n", " 每次请求都实例化:并发量大、请求频繁进会耗尽套接字端口\n", "*/\n", "{ \n", + " var baseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n", + "\n", " using(var client = new HttpClient())\n", " {\n", " //发送请求\n", - " var response = await client.GetAsync(\"http://localhost\");\n", + " var response = await client.GetAsync(baseUrl);\n", " response.EnsureSuccessStatusCode();\n", " }\n", "\n", @@ -169,7 +128,7 @@ " {\n", " using(var client = new HttpClient())\n", " {\n", - " var response = await client.GetAsync(\"http://localhost\");\n", + " var response = await client.GetAsync(baseUrl);\n", " response.EnsureSuccessStatusCode();\n", " displayValue.Update($\"第 {i+1} 次/ 共 10 次请求,成功!\");\n", " }\n", @@ -184,6 +143,239 @@ "## 2、手动管理:静态类或单例" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "相比于直接new,实现了HttpClient的重用,`不推荐的`:\n", + "\n", + "缺点:\n", + " 1. 不够灵活、优雅:特别是有多个系列的请求时;\n", + " \n", + "优点:\n", + " 1. 复用 HttpClient\n", + " 2. 实现了HttpClient的重用,减少创建和销毁的开销" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "/*\n", + " 静态类/属性\n", + "*/\n", + "\n", + "public class HttpClientHelper\n", + "{\n", + " public readonly static HttpClient StaticClient;\n", + "\n", + " static HttpClientHelper()\n", + " {\n", + " SocketsHttpHandler handler = new SocketsHttpHandler()\n", + " {\n", + " PooledConnectionLifetime = TimeSpan.FromSeconds(30),\n", + " };\n", + "\n", + " StaticClient = new HttpClient(handler);\n", + "\n", + " //统一设置:请求头等\n", + "\n", + " //统一错误处理\n", + "\n", + " //当然这里也可以设置Pipline,不过这里就不演示了\n", + " } \n", + "\n", + " public static async Task GetAsync(string url)\n", + " {\n", + " return await StaticClient.GetAsync(url);\n", + " }\n", + "\n", + " public static async Task GetStringAsync(string url)\n", + " {\n", + " var response = await StaticClient.GetAsync(url);\n", + " response.EnsureSuccessStatusCode();\n", + " return await response.Content.ReadAsStringAsync();\n", + " }\n", + "\n", + " public static async Task PostAsync(string url, HttpContent content)\n", + " {\n", + " return await StaticClient.PostAsync(url, content);\n", + " }\n", + "}\n", + "\n", + "{ //调用静态类\n", + " var baseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n", + " var response = await HttpClientHelper.GetAsync(baseUrl+\"/api/Config/GetApiConfig\");\n", + " var content = await response.Content.ReadAsStringAsync();\n", + " Console.WriteLine(content);\n", + "\n", + " var response2 = await HttpClientHelper.GetStringAsync(baseUrl+\"/api/Normal/GetAllAccounts\");\n", + " Console.WriteLine(response2);\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "/*\n", + " 单例实现1:\n", + " 1. 私有构造函数,防止外部实例化\n", + " 2. 使用静态只读变量存储类的实例,由.Net框架保证实例不变且线程安全\n", + " 3. 密封类,拒绝继承,保证不被子类破坏\n", + "*/\n", + "\n", + "// 使用Lazy实现单例\n", + "public sealed class HttpClientSingleton \n", + "{\n", + " // 私有静态变量,用于存储类的实例\n", + " private static readonly HttpClientSingleton instance = new HttpClientSingleton();\n", + " \n", + " //公共静态属性,用于获取类的实例\n", + " public static HttpClientSingleton Instance\n", + " {\n", + " get\n", + " {\n", + " return instance;\n", + " }\n", + " }\n", + "\n", + " private readonly HttpClient Client;\n", + "\n", + " //私有构造函数,防止外部实例化\n", + " private HttpClientSingleton() \n", + " {\n", + " SocketsHttpHandler handler = new SocketsHttpHandler()\n", + " {\n", + " PooledConnectionLifetime = TimeSpan.FromSeconds(30),\n", + " };\n", + "\n", + " Client = new HttpClient(handler);\n", + "\n", + " //统一设置:请求头等\n", + "\n", + " //统一错误处理\n", + "\n", + " //可以使用IoC容器来管理\n", + "\n", + " //当然这里也可以设置Pipline,不过这里就不演示了\n", + " Console.WriteLine(\"HttpClientSingleton 初始化一次\");\n", + " }\n", + "\n", + " public async Task GetAsync(string url)\n", + " {\n", + " return await Client.GetAsync(url);\n", + " }\n", + "\n", + " public async Task GetStringAsync(string url)\n", + " {\n", + " var response = await Client.GetAsync(url);\n", + " response.EnsureSuccessStatusCode();\n", + " return await response.Content.ReadAsStringAsync();\n", + " }\n", + "}\n", + "\n", + "{ //调用示例\n", + "\n", + " var baseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n", + " var response = await HttpClientSingleton.Instance.GetAsync(baseUrl+\"/api/Config/GetApiConfig\");\n", + " var content = await response.Content.ReadAsStringAsync();\n", + " Console.WriteLine(content);\n", + "\n", + " var response2 = await HttpClientSingleton.Instance.GetStringAsync(baseUrl+\"/api/Normal/GetAllAccounts\");\n", + " Console.WriteLine(response2);\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "/*\n", + " 单例实现2:\n", + " 1. 私有构造函数,防止外部实例化\n", + " 2. 使用Lazy, 延迟实例化, 由.Net 框架保证线程安全\n", + " 3. 密封类,拒绝继承,保证不被子类破坏\n", + "*/\n", + "\n", + "// 由于静态初始化器是由 .NET 运行时在后台处理的,因此它是线程安全的,不需要额外的锁定机制。\n", + "public sealed class HttpClientSingleton2\n", + "{\n", + " private static readonly Lazy _httpClientLazy = new Lazy(() =>\n", + " {\n", + " SocketsHttpHandler handler = new SocketsHttpHandler()\n", + " {\n", + " PooledConnectionLifetime = TimeSpan.FromSeconds(30)\n", + " };\n", + "\n", + " var client = new HttpClient(handler)\n", + " {\n", + " // 可以在这里配置HttpClient的实例,例如设置超时时间、基地址等\n", + " //Timeout = TimeSpan.FromSeconds(30),\n", + " //BaseAddress = new Uri(\"https://api.example.com/\"),\n", + " };\n", + "\n", + "\n", + " //统一设置:请求头等\n", + "\n", + " //统一错误处理\n", + "\n", + " //可以使用IoC容器来管理\n", + "\n", + " //当然这里也可以设置Pipline,不过这里就不演示了\n", + " Console.WriteLine(\"HttpClientSingleton2 初始化一次\");\n", + "\n", + " return client;\n", + " });\n", + "\n", + " public static HttpClient Instance => _httpClientLazy.Value;\n", + "\n", + " // 私有构造函数,防止外部实例化\n", + " private HttpClientSingleton2() { } \n", + "\n", + " public async Task GetAsync(string url)\n", + " {\n", + " return await _httpClientLazy.Value.GetAsync(url);\n", + " }\n", + "\n", + " public async Task GetStringAsync(string url)\n", + " {\n", + " var response = await _httpClientLazy.Value.GetAsync(url);\n", + " response.EnsureSuccessStatusCode();\n", + " return await response.Content.ReadAsStringAsync();\n", + " }\n", + "}\n", + "\n", + "{ //调用示例\n", + "\n", + " var baseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n", + " var response = await HttpClientSingleton2.Instance.GetAsync(baseUrl+\"/api/Config/GetApiConfig\");\n", + " var content = await response.Content.ReadAsStringAsync();\n", + " Console.WriteLine(content);\n", + "\n", + " var response2 = await HttpClientSingleton2.Instance.GetStringAsync(baseUrl+\"/api/Normal/GetAllAccounts\");\n", + " Console.WriteLine(response2);\n", + "}" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -191,6 +383,23 @@ "## 3、手动管理:多工具类(每类请求对应一种工具类或单例类)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "把不同类别的请求分成不同的工具类,业务类直接封装成工具类的方法。类似类型化的客户端。 简单使用的话,`比较推荐`\n", + "\n", + "优点:\n", + " 1. 复用HttpClient\n", + " 2. 可以灵活的进行统一配置\n", + " 3. 不同类别不同工具类,方便定制\n", + " 4. 业务直接封装成工具类方法,调用方便、快捷\n", + "\n", + "缺点:\n", + " 1. 工具类比较多,需要手动维护\n", + " 2. 工具类方法比较多且和业务直接相关,需要手动维护\n" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/Docs/1.4.6..高级使用.工厂模式.ipynb b/Docs/1.4.6.高级使用.工厂模式.ipynb similarity index 100% rename from Docs/1.4.6..高级使用.工厂模式.ipynb rename to Docs/1.4.6.高级使用.工厂模式.ipynb