From 975e8d77132a9f99e83307278995e06e43bc73d9 Mon Sep 17 00:00:00 2001
From: bicijinlian <bicijinlian@noreply.gitcode.com>
Date: Tue, 6 Aug 2024 20:48:07 +0800
Subject: [PATCH] =?UTF-8?q?=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 | 450 ++++++++++++------
 .../HttpClientStudy.Core.csproj               |   1 +
 HttpClientStudy.UnitTest/TempTest.cs          |  50 +-
 3 files changed, 364 insertions(+), 137 deletions(-)

diff --git a/Docs/1.3.0.基础使用.管理客户端.ipynb b/Docs/1.3.0.基础使用.管理客户端.ipynb
index 6885467..2732b8c 100644
--- a/Docs/1.3.0.基础使用.管理客户端.ipynb
+++ b/Docs/1.3.0.基础使用.管理客户端.ipynb
@@ -23,9 +23,16 @@
     "为实现复用,HttpClient类库默认使用连接池和请求管道,可以手动管理(连接池、配置管道、使用Polly); 结合IoC容器、工厂模式(提供了IHttpClientFactory类库)、复原库Polly,可以更加方便、完善的使用,这也是推荐的方法。"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 0、初始化与全局设置"
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": 67,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -41,7 +48,7 @@
     {
      "data": {
       "text/html": [
-       "<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.DependencyInjection, 8.0.0</span></li><li><span>System.Net.Http.Json, 8.0.0</span></li></ul></div></div>"
+       "<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.DependencyInjection, 8.0.0</span></li><li><span>Microsoft.Extensions.Http, 8.0.0</span></li><li><span>Microsoft.Extensions.Http.Polly, 8.0.7</span></li><li><span>Polly, 8.4.1</span></li><li><span>System.Net.Http.Json, 8.0.0</span></li></ul></div></div>"
       ]
      },
      "metadata": {},
@@ -57,7 +64,7 @@
     {
      "data": {
       "text/plain": [
-       "d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe"
+       "e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe"
       ]
      },
      "metadata": {},
@@ -67,18 +74,53 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "程序[d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\r\n"
+      "程序[e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\r\n"
      ]
     }
    ],
    "source": [
     "//全局设置,行运行一次,为后续准备\n",
     "#r \"nuget:System.Net.Http.Json\"\n",
+    "#r \"nuget:Microsoft.Extensions.Http\"\n",
     "#r \"nuget:Microsoft.Extensions.DependencyInjection\"\n",
+    "#r \"nuget:Polly\"\n",
+    "#r \"nuget:Microsoft.Extensions.Http.Polly\"\n",
     "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
     "\n",
+    "global using System;\n",
+    "global using System.IO;\n",
+    "global using System.IO.Enumeration;\n",
+    "global using System.Buffers;\n",
+    "\n",
+    "global using System.Collections;\n",
+    "global using System.Collections.Concurrent;\n",
+    "global using System.Linq;\n",
+    "global using System.Linq.Expressions;\n",
+    "global using System.Threading;\n",
+    "global using System.Threading.Tasks;\n",
+    "\n",
     "global using System.Net.Http;\n",
     "global using System.Net.Http.Headers;\n",
+    "\n",
+    "global using Microsoft.Extensions.DependencyInjection;\n",
+    "global using Microsoft.Extensions.DependencyInjection.Extensions;\n",
+    "\n",
+    "global using Polly;\n",
+    "global using Polly.NoOp;\n",
+    "global using Polly.Simmy;\n",
+    "global using Polly.Retry;\n",
+    "global using Polly.Hedging;\n",
+    "global using Polly.Timeout;\n",
+    "global using Polly.Bulkhead;\n",
+    "global using Polly.Fallback;\n",
+    "global using Polly.RateLimit;\n",
+    "global using Polly.CircuitBreaker;\n",
+    "global using Polly.Utilities;\n",
+    "global using Polly.Extensions;\n",
+    "global using Polly.Wrap;\n",
+    "global using Polly.Registry;\n",
+    "global using Polly.Telemetry;\n",
+    "\n",
     "global using HttpClientStudy.Config;\n",
     "global using HttpClientStudy.Core;\n",
     "global using HttpClientStudy.Core.Utilities;\n",
@@ -127,7 +169,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -139,24 +181,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\r\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "第 10 次/ 共 10 次请求,成功!"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
+   "outputs": [],
    "source": [
     "/*\n",
     "    每次请求都实例化:并发量大、请求频繁进会耗尽套接字端口\n",
@@ -209,7 +234,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -221,17 +246,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
-      "{\"data\":{\"host\":\"localhost\",\"port\":5189,\"scheme\":\"http\",\"pathBase\":\"\",\"baseUrl\":\"http://localhost:5189\",\"webAppMutexName\":\"HttpClientStudy.WebApp\"},\"code\":1,\"message\":\"成功\"}\n",
-      "{\"data\":[{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":2,\"name\":\"管理员02\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":3,\"name\":\"管理员03\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":4,\"name\":\"管理员04\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":5,\"name\":\"管理员05\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":6,\"name\":\"管理员06\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":7,\"name\":\"管理员07\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":8,\"name\":\"管理员08\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":9,\"name\":\"管理员09\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":10,\"name\":\"管理员10\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":11,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":12,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":13,\"name\":\"开发03\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":14,\"name\":\"开发04\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":15,\"name\":\"开发05\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":16,\"name\":\"开发06\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":17,\"name\":\"开发07\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":18,\"name\":\"开发08\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":19,\"name\":\"开发09\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":20,\"name\":\"开发10\",\"password\":\"123456\",\"role\":\"Dev\"}],\"code\":1,\"message\":\"成功\"}\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "/*\n",
     "    静态类/属性\n",
@@ -288,7 +303,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -300,18 +315,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
-      "HttpClientSingleton 初始化一次\n",
-      "{\"data\":{\"host\":\"localhost\",\"port\":5189,\"scheme\":\"http\",\"pathBase\":\"\",\"baseUrl\":\"http://localhost:5189\",\"webAppMutexName\":\"HttpClientStudy.WebApp\"},\"code\":1,\"message\":\"成功\"}\n",
-      "{\"data\":[{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":2,\"name\":\"管理员02\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":3,\"name\":\"管理员03\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":4,\"name\":\"管理员04\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":5,\"name\":\"管理员05\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":6,\"name\":\"管理员06\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":7,\"name\":\"管理员07\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":8,\"name\":\"管理员08\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":9,\"name\":\"管理员09\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":10,\"name\":\"管理员10\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":11,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":12,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":13,\"name\":\"开发03\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":14,\"name\":\"开发04\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":15,\"name\":\"开发05\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":16,\"name\":\"开发06\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":17,\"name\":\"开发07\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":18,\"name\":\"开发08\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":19,\"name\":\"开发09\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":20,\"name\":\"开发10\",\"password\":\"123456\",\"role\":\"Dev\"}],\"code\":1,\"message\":\"成功\"}\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "/*\n",
     "   单例实现1:\n",
@@ -384,7 +388,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -396,18 +400,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
-      "HttpClientSingleton2 初始化一次\n",
-      "{\"data\":{\"host\":\"localhost\",\"port\":5189,\"scheme\":\"http\",\"pathBase\":\"\",\"baseUrl\":\"http://localhost:5189\",\"webAppMutexName\":\"HttpClientStudy.WebApp\"},\"code\":1,\"message\":\"成功\"}\n",
-      "{\"data\":[{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":2,\"name\":\"管理员02\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":3,\"name\":\"管理员03\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":4,\"name\":\"管理员04\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":5,\"name\":\"管理员05\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":6,\"name\":\"管理员06\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":7,\"name\":\"管理员07\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":8,\"name\":\"管理员08\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":9,\"name\":\"管理员09\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":10,\"name\":\"管理员10\",\"password\":\"123456\",\"role\":\"Admin\"},{\"id\":11,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":12,\"name\":\"开发01\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":13,\"name\":\"开发03\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":14,\"name\":\"开发04\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":15,\"name\":\"开发05\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":16,\"name\":\"开发06\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":17,\"name\":\"开发07\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":18,\"name\":\"开发08\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":19,\"name\":\"开发09\",\"password\":\"123456\",\"role\":\"Dev\"},{\"id\":20,\"name\":\"开发10\",\"password\":\"123456\",\"role\":\"Dev\"}],\"code\":1,\"message\":\"成功\"}\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "/*\n",
     "   单例实现2:\n",
@@ -502,7 +495,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -514,15 +507,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "401173\r\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "// 百度服务类\n",
     "public sealed class BaiduService \n",
@@ -564,7 +549,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -576,16 +561,7 @@
      "languageId": "polyglot-notebook"
     }
    },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
-      "{\"data\":\"操作成功\",\"code\":1,\"message\":\"成功\"}\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "// 本机服务类\n",
     "// 百度服务类\n",
@@ -635,7 +611,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": null,
    "metadata": {
     "dotnet_interactive": {
      "language": "csharp"
@@ -647,77 +623,228 @@
      "languageId": "polyglot-notebook"
     }
    },
+   "outputs": [],
+   "source": [
+    "#r \"nuget:Polly\"\n",
+    "#r \"nuget:Microsoft.Extensions.Http.Polly\"\n",
+    "\n",
+    "using Polly;\n",
+    "using Polly.Simmy;\n",
+    "using Polly.Retry;\n",
+    "using Polly.Extensions;\n",
+    "{\n",
+    "    var pipleLine = new ResiliencePipelineBuilder()\n",
+    "        .AddRetry(new RetryStrategyOptions()\n",
+    "        {\n",
+    "            ShouldHandle = new PredicateBuilder().Handle<Exception>(),\n",
+    "            MaxRetryAttempts = 3, // Retry up to 3 times\n",
+    "            OnRetry = args =>\n",
+    "            {\n",
+    "                // Due to how we have defined ShouldHandle, this delegate is called only if an exception occurred.\n",
+    "                // Note the ! sign (null-forgiving operator) at the end of the command.\n",
+    "                var exception = args.Outcome.Exception!; // The Exception property is nullable\n",
+    "                Console.WriteLine(\"内部重试\");\n",
+    "                return default;\n",
+    "            }\n",
+    "        })\n",
+    "        .Build();\n",
+    "\n",
+    "    var BaseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
+    "    HttpClient client = new HttpClient(new SocketsHttpHandler(){})\n",
+    "    {\n",
+    "        BaseAddress = new Uri(BaseUrl),\n",
+    "    };\n",
+    "\n",
+    "    try\n",
+    "    {\n",
+    "        await pipleLine.ExecuteAsync(async (inneerToken)=>\n",
+    "        {\n",
+    "            var response = await client.GetAsync(\"api/Polly8/Retry_Exception\",inneerToken);\n",
+    "            response.EnsureSuccessStatusCode();\n",
+    "        });\n",
+    "    }\n",
+    "    catch(Exception ex)\n",
+    "    {\n",
+    "        Console.WriteLine(ex.Message);\n",
+    "    }\n",
+    "    finally\n",
+    "    {\n",
+    "\n",
+    "    }\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 5、IoC容器管理"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### 直接注册IoC"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "metadata": {
+    "vscode": {
+     "languageId": "polyglot-notebook"
+    }
+   },
    "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.Http.Polly, 8.0.7</span></li><li><span>Polly, 8.4.1</span></li></ul></div></div>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
-      "内部重试\n",
-      "内部重试\n",
-      "内部重试\n",
-      "Response status code does not indicate success: 500 (Internal Server Error).\n"
+      "9193\n",
+      "9193\n",
+      "18\n"
      ]
     }
    ],
    "source": [
-    "#r \"nuget:Polly\"\n",
-    "#r \"nuget:Microsoft.Extensions.Http.Polly\"\n",
+    "/*\n",
+    "    注意:\n",
+    "        1、直接IoC管理:只能一个,不太方便;\n",
+    "        2、可使用.NET 8+ 的 KeyedService, 可以管理多个。 老版只能用服务集合,勉强能用;\n",
+    "        3、把HttpClient 放在多个类中,分别注册使用;不过这样,不如直接使用类型化客户端;\n",
+    "*/\n",
+    "{   // 直接使用\n",
+    "    var services = new ServiceCollection();\n",
+    "    services.AddSingleton<HttpClient>(new HttpClient()\n",
+    "    {\n",
+    "        //BaseAddress = new Uri(\"https://localhost:5001/\"),\n",
+    "        Timeout = TimeSpan.FromSeconds(10),\n",
+    "    });\n",
     "\n",
-    "using Polly;\n",
-    "using Polly.Simmy;\n",
-    "using Polly.Retry;\n",
-    "using Polly.Extensions;\n",
+    "    var client = services.BuildServiceProvider().GetRequiredService<HttpClient>();\n",
     "\n",
-    "var pipleLine = new ResiliencePipelineBuilder()\n",
-    "    .AddRetry(new RetryStrategyOptions()\n",
-    "    {\n",
-    "        ShouldHandle = new PredicateBuilder().Handle<Exception>(),\n",
-    "        MaxRetryAttempts = 3, // Retry up to 3 times\n",
-    "        OnRetry = args =>\n",
+    "    var resp = await client.GetAsync(\"https://www.baidu.com\");\n",
+    "    resp.EnsureSuccessStatusCode();\n",
+    "\n",
+    "    var content = await resp.Content.ReadAsStringAsync();\n",
+    "    Console.WriteLine(content.Length);\n",
+    "}\n",
+    "\n",
+    "{ // KeyService: .Net 8+ 才支持的功能\n",
+    "    var services = new ServiceCollection();\n",
+    "    services\n",
+    "        .AddKeyedSingleton<HttpClient>(\"HttpClientA\",new HttpClient()\n",
     "        {\n",
-    "            // Due to how we have defined ShouldHandle, this delegate is called only if an exception occurred.\n",
-    "            // Note the ! sign (null-forgiving operator) at the end of the command.\n",
-    "            var exception = args.Outcome.Exception!; // The Exception property is nullable\n",
-    "            Console.WriteLine(\"内部重试\");\n",
-    "            return default;\n",
-    "        }\n",
-    "    })\n",
-    "    .Build();\n",
+    "            BaseAddress = new Uri(\"https://www.baidu.com/\"),\n",
+    "            Timeout = TimeSpan.FromSeconds(10),\n",
+    "        })\n",
+    "        .AddKeyedSingleton<HttpClient>(\"HttpClientB\", new HttpClient()\n",
+    "        {\n",
+    "            BaseAddress = new Uri(\"https://www.qq.com/\"),\n",
+    "            Timeout = TimeSpan.FromSeconds(2),\n",
+    "        });\n",
     "\n",
-    "var BaseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
-    "HttpClient client = new HttpClient(new SocketsHttpHandler()\n",
-    "{\n",
-    "    \n",
-    "})\n",
-    "{\n",
-    "    BaseAddress = new Uri(BaseUrl),\n",
-    "};\n",
+    "    var clientA = services.BuildServiceProvider().GetRequiredKeyedService<HttpClient>(\"HttpClientA\");\n",
+    "    var responseA = await clientA.GetAsync(\"/\");\n",
+    "    responseA.EnsureSuccessStatusCode();\n",
+    "    var contentA = await responseA.Content.ReadAsStringAsync();\n",
+    "    Console.WriteLine(contentA.Length);\n",
+    "\n",
+    "    var clientB = services.BuildServiceProvider().GetRequiredKeyedService<HttpClient>(\"HttpClientB\");\n",
+    "\n",
+    "    var responseB = await clientB.GetAsync(\"/\");\n",
+    "    responseB.EnsureSuccessStatusCode();\n",
+    "\n",
+    "    var contentB= await responseB.Content.ReadAsStringAsync();\n",
+    "    Console.WriteLine(contentB.Length);\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### HttpClient 多服务类"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 55,
+   "metadata": {
+    "vscode": {
+     "languageId": "polyglot-notebook"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "A: 9508\n",
+      "A: 18\n"
+     ]
+    }
+   ],
+   "source": [
+    "// IoC 多个HttpClient服务类\n",
     "\n",
-    "try\n",
+    "public class  HttpClientServerA\n",
     "{\n",
-    "    await pipleLine.ExecuteAsync(async (inneerToken)=>\n",
+    "    public static HttpClient Client = new HttpClient()\n",
+    "    {\n",
+    "        BaseAddress = new Uri(\"https://www.baidu.com/\"),\n",
+    "        Timeout = TimeSpan.FromSeconds(2),\n",
+    "    };\n",
+    "\n",
+    "    public int GetBaiduIndexLength()\n",
     "    {\n",
-    "        var response = await client.GetAsync(\"api/Polly8/Retry_Exception\",inneerToken);\n",
+    "        var requestMessage = new HttpRequestMessage(HttpMethod.Get, \"/\");\n",
+    "\n",
+    "        var response = Client.Send(requestMessage);\n",
+    "\n",
     "        response.EnsureSuccessStatusCode();\n",
-    "    });\n",
+    "\n",
+    "        var s = response.Content.ReadAsStream();\n",
+    "        return (int)s.Length;\n",
+    "    }\n",
     "}\n",
-    "catch(Exception ex)\n",
+    "\n",
+    "public class  HttpClientServerB\n",
     "{\n",
-    "    Console.WriteLine(ex.Message);\n",
+    "    public static HttpClient Client = new HttpClient()\n",
+    "    {\n",
+    "        BaseAddress = new Uri(\"https://www.qq.com/\"),\n",
+    "        Timeout = TimeSpan.FromSeconds(2),\n",
+    "    };\n",
+    "\n",
+    "    public int GetBaiduIndexLength()\n",
+    "    {\n",
+    "        var requestMessage = new HttpRequestMessage(HttpMethod.Get, \"/\");\n",
+    "\n",
+    "        var response = Client.Send(requestMessage);\n",
+    "\n",
+    "        response.EnsureSuccessStatusCode();\n",
+    "\n",
+    "        var s = response.Content.ReadAsStream();\n",
+    "        return (int)s.Length;\n",
+    "    }\n",
     "}\n",
-    "finally\n",
+    "\n",
     "{\n",
+    "    var services = new ServiceCollection();\n",
+    "    services.AddScoped<HttpClientServerA>();\n",
+    "    services.AddScoped<HttpClientServerB>();\n",
+    "\n",
+    "    var provider = services.BuildServiceProvider();\n",
+    "\n",
+    "    var clientA = provider.GetService<HttpClientServerA>();\n",
+    "    var sumA = clientA.GetBaiduIndexLength();\n",
+    "    Console.WriteLine($\"A: {sumA}\");\n",
     "\n",
+    "    var clientB = provider.GetService<HttpClientServerB>();\n",
+    "    var sumB = clientB.GetBaiduIndexLength();\n",
+    "    Console.WriteLine($\"A: {sumB}\");\n",
     "}"
    ]
   },
@@ -725,14 +852,67 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "## 5、IoC容器管理:类型化的客户端"
+    "## 6、客户端工厂管理:IHttpClientFactory(需要结合IoC) `强力推荐`"
    ]
   },
   {
    "cell_type": "markdown",
-   "metadata": {},
+   "metadata": {
+    "vscode": {
+     "languageId": "polyglot-notebook"
+    }
+   },
    "source": [
-    "## 6、客户端工厂管理:IHttpClientFactory(需要结合IoC)"
+    "使用 IHttpClientFactory 创建和管理 `短期HttpClient` 是官方强力推荐的方式。特别是使用IoC或是 ASP.NET中后台调用其它接口的情况。\n",
+    "\n",
+    "IHttpClientFactory 综合使用了 HttpClient的多种特性:HttpClient的生命周期、HttpClient的配置、HttpClient的拦截器、HttpClient的缓存、HttpClient的依赖注入、Polly等等。\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 68,
+   "metadata": {
+    "vscode": {
+     "languageId": "polyglot-notebook"
+    }
+   },
+   "outputs": [
+    {
+     "ename": "Error",
+     "evalue": "System.InvalidOperationException: An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set.\r\n   at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request)\r\n   at System.Net.Http.HttpClient.GetAsync(String requestUri)\r\n   at Submission#69.<<Initialize>>d__0.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)",
+     "output_type": "error",
+     "traceback": [
+      "System.InvalidOperationException: An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set.\r\n",
+      "   at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request)\r\n",
+      "   at System.Net.Http.HttpClient.GetAsync(String requestUri)\r\n",
+      "   at Submission#69.<<Initialize>>d__0.MoveNext()\r\n",
+      "--- End of stack trace from previous location ---\r\n",
+      "   at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)"
+     ]
+    }
+   ],
+   "source": [
+    "//基础使用\n",
+    "{\n",
+    "    var services = new ServiceCollection();\n",
+    "    services.AddHttpClient<HttpClient>((provider, client) => \n",
+    "    {\n",
+    "        client.BaseAddress = new Uri(\"https://www.baidu.com\");\n",
+    "    });\n",
+    "\n",
+    "    var provider = services.BuildServiceProvider();\n",
+    "\n",
+    "    var factory = provider.GetRequiredService<IHttpClientFactory>();\n",
+    "\n",
+    "    var sum = factory.CreateClient().GetAsync(\"/\").Result.Content.ReadAsStringAsync().Result.Length;\n",
+    "    \n",
+    "    Console.WriteLine(sum);\n",
+    "}\n",
+    "\n",
+    "//\n",
+    "{\n",
+    "\n",
+    "}"
    ]
   },
   {
diff --git a/HttpClientStudy.Core/HttpClientStudy.Core.csproj b/HttpClientStudy.Core/HttpClientStudy.Core.csproj
index fe133c2..0092c2e 100644
--- a/HttpClientStudy.Core/HttpClientStudy.Core.csproj
+++ b/HttpClientStudy.Core/HttpClientStudy.Core.csproj
@@ -7,6 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
     <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.1" />
     <PackageReference Include="Polly" Version="8.2.1" />
     <PackageReference Include="Polly.Caching.IDistributedCache" Version="1.0.4" />
diff --git a/HttpClientStudy.UnitTest/TempTest.cs b/HttpClientStudy.UnitTest/TempTest.cs
index eed1aa1..60120c8 100644
--- a/HttpClientStudy.UnitTest/TempTest.cs
+++ b/HttpClientStudy.UnitTest/TempTest.cs
@@ -6,6 +6,8 @@ using System.Threading.Tasks;
 
 using HttpClientStudy.Core.Utilities;
 
+using Microsoft.Extensions.DependencyInjection;
+
 namespace HttpClientStudy.UnitTest
 {
     /// <summary>
@@ -20,9 +22,53 @@ namespace HttpClientStudy.UnitTest
         }
 
         [Fact]
-        public void Test()
+        public async Task TestAsync()
+        {
+            var services = new ServiceCollection();
+            services.AddKeyedSingleton<HttpClient>("HttpClientA",new HttpClient()
+            {
+                //BaseAddress = new Uri("https://localhost:5001/"),
+                Timeout = TimeSpan.FromSeconds(10),
+            });
+
+            var client = services.BuildServiceProvider().GetRequiredKeyedService<HttpClient>("HttpClientA");
+
+            var resp = await client.GetAsync("https://www.baidu.com");
+            resp.EnsureSuccessStatusCode();
+
+            var content = await resp.Content.ReadAsStringAsync();
+            Console.WriteLine(content.Length);
+        }
+
+        [Fact]
+        public async Task Temp_TestAsync()
         {
-            
+            var services = new ServiceCollection();
+            //services.AddHttpClient();
+            services
+                .AddHttpClient<HttpClient>(config =>
+                {
+                    config.BaseAddress = new Uri("https://www.baidu.com");
+                })
+                .ConfigureHttpClient(config =>
+                {
+                    config.BaseAddress = new Uri("https://www.qq.com");
+                });
+
+
+
+            var provider = services.BuildServiceProvider();
+
+            var factory = provider.GetRequiredService<IHttpClientFactory>();
+
+            var client = factory.CreateClient();
+
+            var res = await client.GetAsync("https://www.qq.com");
+            res.EnsureSuccessStatusCode();
+            var content = await res.Content.ReadAsStringAsync();
+
+            var sum = content.Length;
+            _output.WriteLine(sum.ToString());
         }
     }
 }