《ASP.NET Core 6框架揭秘》实例演示[18]:HttpClient处理管道

在《《ASP.NET Core 6框架揭秘》实例演示[17]:利用IHttpClientFactory工厂来创建HttpClient》之后,我们将关注点放到HttpClient对象上。我们知道ASP.NET的核心就是由中间件组成的请求处理管道,HttpClient也采用了类似的设计。HttpClient管道由一组HttpMessageHandler对象构成,这些HttpMessageHandler相当于ASPNET的中间件。如下这些示例演示帮助我们更清楚地认识HttpMessageHandler处理管道。[本文节选《ASP.NET Core 6框架揭秘》第12章]

[S1208]HttpClient的默认管道结构(源代码)
[S1209]定制HttpClient管道(源代码)
[S1210]针对HTTP调用的日志输出(>=Information)(源代码)
[S1211]针对HTTP调用的日志输出(>=Trace)(源代码)

[S1208]HttpClient的默认管道结构

接下来我们通过如下的演示程序使用IHttpClientFactory工厂创建了 一个HttpClient对象,并查看其管道依次由哪些类型的HttpMessageHandler对象组成。如代码片段所示,我们定义了一个辅助方法PrintPipeline方法以递归的形式将指定HttpMessageHandler对象及其下一个处理器的类型输出到控制台上。

using Microsoft.Extensions.DependencyInjection;
using System.Reflection;var httpClient = new ServiceCollection().AddHttpClient().BuildServiceProvider().GetRequiredService<IHttpClientFactory>().CreateClient();
var handlerField = typeof(HttpMessageInvoker).GetField("_handler", BindingFlags.NonPublic | BindingFlags.Instance);
PrintPipeline((HttpMessageHandler?)handlerField?.GetValue(httpClient), 0);static void PrintPipeline(HttpMessageHandler? handler, int index)
{if (index == 0){Console.WriteLine(handler?.GetType().Name);}else{Console.WriteLine($"{new string(' ', index * 4)}=>{handler?.GetType().Name}");}if (handler is DelegatingHandler delegatingHandler){PrintPipeline(delegatingHandler.InnerHandler, index + 1);}
}

我们利用依赖注入容器提供的IHttpClientFactory工厂创建出HttpClient对象,并利用反射方式得到表示处理器的HttpMessageHandler对象,它实际上就是管道的第一个DelegatingHandler对象。我们将这个对象作为参数调用PrintPipeline方法将构成管道的每个处理器类型名称打印出来,图1为最终的输出结果。

372031c1c387f3d82f312755cd37f230.png
图1 默认处理器管道

从图1所示的输出结果可以看出,对于采用默认配置构建的IHttpClientFactory工厂创建的HttpClient对象来说,它的处理器管道由如下四个类型的处理器构成:

  • LifetimeTrackingHttpMessageHandler:在指定的生命周期内复用HttpMessageHandler对象的以提供更好的性能。

  • LoggingScopeHttpMessageHandler:在整个调用的边界(从开始调用到返回结果)输出相应的跟踪诊断日志(比如记录整个调用耗时)。

  • LoggingHttpMessageHandler:在网络交互边界(从请求发送到响应接收)输出相应的跟踪诊断日志(比如单纯记录网络通信耗时)。

  • HttpClientHandler:完成基于网络传输的请求发送和响应接收。

[S1209]定制HttpClient管道

对于任何一个由IHttpClientFactory工厂创建的HttpClient对象来说,除了位于管道末端作为主处理器的HttpClientHandler可以替换之外,上述的其它三个处理器总是存在的。我们可以通过配置添加为构建的管道上添加任意处理器,它们最终会被添加到LoggingScopeHttpMessageHandler和LoggingHttpMessageHandler之间。我们编写了一个简单的实例来演示针对自定义处理器的注册。如下面的代码片段所示,我们定义了四个HttpMessageHandler类型,其中派生于HttpClientHandler的ExtendedHttpClientHandler将作为管道末端的主处理器,其他三个派生于DelegatingHandler的处理器将额外“注入”管道中。

public class ExtendedHttpClientHandler     : HttpClientHandler { }
public class FooHttpMessageHandler : DelegatingHandler { }
public class BarHttpMessageHandler : DelegatingHandler { }
public class BazHttpMessageHandler : DelegatingHandler { }
如下所示的演示程序在调用AddClient扩展方法得到返回的IHttpClientBuilder对象之后,调用了它的ConfigurePrimaryHttpMessageHandler扩展方法,并利用提供了一个Func<HttpMessageHandler>委托将ExtendedHttpClientHandler对象注册为主处理器。我们接下来调用了这个IHttpClientBuilder对象的AddHttpMessageHandler扩展方法利用提供的Func<IServiceProvider, DelegatingHandler>委托添加了额外的三个处理器。
using App;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;var services = new ServiceCollection();
services.AddHttpClient(string.Empty).ConfigurePrimaryHttpMessageHandler(_ => new ExtendedHttpClientHandler()).AddHttpMessageHandler(_ => new FooHttpMessageHandler()).AddHttpMessageHandler(_ => new BarHttpMessageHandler()).AddHttpMessageHandler(_ => new BazHttpMessageHandler());var httpClient = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>().CreateClient();
var handlerField = typeof(HttpMessageInvoker).GetField("_handler", BindingFlags.NonPublic | BindingFlags.Instance);
PrintPipeline((HttpMessageHandler?)handlerField?.GetValue(httpClient), 0);static void PrintPipeline(HttpMessageHandler? handler, int index)
{if (index == 0){Console.WriteLine(handler?.GetType().Name);}else{Console.WriteLine($"{new string(' ', index * 4)}=>{handler?.GetType().Name}");}if (handler is DelegatingHandler delegatingHandler){PrintPipeline(delegatingHandler.InnerHandler, index + 1);}
}

在利用IServiceProvider对象构建出IHttpClientFactory工厂之后,我们利用它将HttpClient对象创建出来,并采用与前一个实例相同的方式将它的处理器管道结构打印出来。组成管道的处理器顺序体现在如图2所示的输出结果中。

095db20f961ad3f2020b80bff56d25e4.png
图2 定制处理器管道

[S1210]针对HTTP调用的日志输出(>=Information)

对于由IHttpClientFactory工厂创建的HttpClient来说,它的处理器管道总是包含两个与日志相关的处理器,对应的类型分别是LoggingScopeHttpMessageHandler和LoggingHttpMessageHandler,它们会在不同的边界或范围输出相应的跟踪诊断日志。前者的边界是针对的是基于整个管道的调用,后者则是针对的是最后一个面向网络传输。它们究竟会输出怎样的日志呢?我们不妨通过一个简单的实例来寻找答案。如下面代码片段所示,我们自定义了一个继承自DelegatingHandler的DelayHttpMessageHanadler类型,它会在调用后续处理器前后模拟1秒和2秒的耗时。

public class DelayHttpMessageHanadler : DelegatingHandler
{protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);var response = await base.SendAsync(request, cancellationToken);await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken);return response;}
}

在调用AddHttpClient扩展方法对DelayHttpMessageHanadler进行注册之前,我们还添加了针对日志的服务注册。具体来说,我们添加了针对控制台的输出,并开启了针对日志范围的支持。在利用IHttpClientFactory工厂将HttpClient对象创建出来后,我们用它向地址“http://www.baidu.com”发送了一个GET请求。

using App;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;var services = new ServiceCollection().AddLogging(logging => logging.AddConsole().AddSimpleConsole(options => options.IncludeScopes = true));
services.AddHttpClient(string.Empty).AddHttpMessageHandler(() => new DelayHttpMessageHanadler());
var httpClient = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>().CreateClient();
await httpClient.GetAsync("http://www.baidu.com");

程序运行之后,我们会在控制台上看到如图3所示的四条日志。日志第一条和最后一条是LoggingScopeHttpMessageHandler输出的,它创建了一个日志范围,范围名称采用模板为“HTTP {Method} {URL}”,最后一条日志会输出针对整个管道上的调用耗时。第2条和第3条日志是LoggingHttpMessageHandler对象输出的,它们写入的时机分别是发送请求前和接收到请求后,最后一条还是输出两者之间的时间间隔,也就是面向网络传输的耗时。从输出的内容可以看出,两个耗时基本上相差三秒,刚好是我们注册的DelayHttpMessageHanadler对象模拟延时。

5812be52dc64ad1abe69d85dde4031b9.png
图3 诊断日志(Level >=Information)

[S1211]针对HTTP调用的日志输出(>=Trace)

由于在默认情况下只有等级不低于Information的日志才会输出到控制台上,所以看不到上述两个输出的更低等级(Trace)的日志。接下来我们对程序作如下的改动,通过添加日志过滤器输出所有等级的日志。

using App;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;var services = new ServiceCollection().AddLogging(logging => logging.SetMinimumLevel(LogLevel.Trace).AddConsole().AddSimpleConsole(options => options.IncludeScopes = true));
services.AddHttpClient(string.Empty).AddHttpMessageHandler(() => new DelayHttpMessageHanadler());
var httpClient = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>().CreateClient();
await httpClient.GetAsync("http://www.baidu.com");

再次运行我们的演示程序,控制台上将会输出如图4所示的日志。我们可以看出LoggingScopeHttpMessageHandler和LoggingHttpMessageHandler会将请求和响应的报头写入到等级为Trace的日志之中。

caa2aa4590f6ce8d72b22798124fc1f4.png
图4 诊断日志(All)

《ASP.NET Core 6框架揭秘》实例演示[01]:编程初体验
《ASP.NET Core 6框架揭秘》实例演示[02]:各种形式的API开发
《ASP.NET Core 6框架揭秘》实例演示[03]:Dapr初体验
《ASP.NET Core 6框架揭秘》实例演示[04]:自定义依赖注入框架
《ASP.NET Core 6框架揭秘》实例演示[05]:依赖注入基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[06]:依赖注入框架设计细节
《ASP.NET Core 6框架揭秘》实例演示[07]:文件系统
《ASP.NET Core 6框架揭秘》实例演示[08]:配置的基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[09]:将配置绑定为对象
《ASP.NET Core 6框架揭秘》实例演示[10]:Options基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[11]:诊断跟踪的几种基本编程方式 
《ASP.NET Core 6框架揭秘》实例演示[13]:日志的基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[14]:日志的进阶用法
《ASP.NET Core 6框架揭秘》实例演示[15]:针对控制台的日志输出
《ASP.NET Core 6框架揭秘》实例演示[16]:内存缓存与分布式缓存的使用
《ASP.NET Core 6框架揭秘》实例演示[17]:利用IHttpClientFactory工厂来创建HttpClient

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283141.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯云副总裁答治茜:移动互联网破局要借助“三张网”

5月24日&#xff0c;2018腾讯云未来峰会在广州召开。在互联网专场上&#xff0c;腾讯云副总裁答治茜就泛互联网行业云化的主题发表演讲。在演讲中答治茜表示&#xff0c;过去移动互联网的高速增长到现在已经遇到了一个天花板&#xff0c;需要借助马化腾提到的“人联网、物联网、…

雅诗兰黛天猫超级品牌日:未央唇膏、红装小棕瓶“当红不让”

随着年末圣诞季的临近&#xff0c;各大美妆品牌陆续推出了圣诞套装&#xff0c;红红火火的超豪华套装&#xff0c;算是对用户最实在的回馈。高端美妆品牌的“领头羊”雅诗兰黛&#xff0c;当然也“当红不让”&#xff0c;趁着圣诞季&#xff0c;与天猫超级品牌日联手打造了一场…

JAVA常见算法题(三十一)---冒泡排序

package com.jege.spring.boot.hello.world;/*** java算法之冒泡排序<br>* 将数组按照从大到小的顺序排列<br>* * * author Administrator**/ public class BubbleSort{public static void main(String[] args){int score[] {67, 69, 75, 87, 89, 90, 99, 100};fo…

java 1.7的新特性_[Java]  JDK 1.7版本的 新特性

在网上看到一些jdk1.7的新特性&#xff0c;现将我觉得比较实用的记录于下&#xff1a;(1)switch中可以使用字串了String s "test";switch (s) {case "test" :System.out.println("test");case "test1" :System.out.println("tes…

WPF实现物理效果 拉一个小球

原文:WPF实现物理效果 拉一个小球一直以来都对物理效果有神秘感,完全不知道怎么实现的.直到看到了周银辉在老早前写的一篇博客:http://www.cnblogs.com/zhouyinhui/archive/2007/06/23/793724.html 终于知道是怎么实现的了. CompositionTarget类的Rendering事件.在每一帧成功渲…

C# CM框架下一行代码实现多页面管理

概述之前我分享过一个wpf的项目实践&#xff0c;主页面左侧是个listbox&#xff0c;每次选择改变后呈现对应的页面&#xff0c;界面图如下&#xff1a;要实现这样一个功能&#xff0c;我之前是采用传统方式实现的&#xff0c;本节我采用CM框架下的Conductor<T>去实现&…

如何用DW设计界面 结合 VS设计后台代码

原文发布时间为&#xff1a;2008-11-02 —— 来源于本人的百度文章 [由搬家工具导入]问&#xff1a;在vs.net里有form标记&#xff0c;而dw里却没有&#xff0c;两个里面的标记代码都不一样&#xff0c;怎么能通用&#xff1f; 在.net里修改dw的文件&#xff0c;或在dw里修改a…

java中instanceof 详解_java中的instanceof用法详解

instanceof是Java的一个二元操作符(运算符),也是Java的保留关键字。它的作用是判断其左边对象是否为其右边类的实例&#xff0c;返回的是boolean类型的数据。用它来判断某个对象是否是某个Class类的实例。用法&#xff1a;boolean result object instanceof class参数&#xf…

关于JAVA异常处理的20个最佳实践

关于JAVA异常处理的20个最佳实践 在我们深入了解异常处理最佳实践的深层概念之前&#xff0c;让我们从一个最重要的概念开始&#xff0c;那就是理解在JAVA中有三种一般类型的可抛类: 检查性异常(checked exceptions)、非检查性异常(unchecked Exceptions) 和 错误(errors)。 异…

Atitit Data Matrix dm码的原理与特点

Atitit Data Matrix dm码的原理与特点 Datamatrix原名Datacode&#xff0c;由美国国际资料公司(International Data Matrix, 简称ID Matrix)于1989年发明。 Datamatrix是一种矩阵式二维条码&#xff0c;其发展的构想是希望在较小的条码标签上存入更多的资料量。Datamatrix的最小…

SSM框架搭建(四) springmvc和mybatis的配置

SSM框架搭建&#xff08;一&#xff09; JDK和MAVEN环境搭建 SSM框架搭建&#xff08;二&#xff09; 创建MAVEN项目 SSM框架搭建&#xff08;三&#xff09; 数据库创建和MyBatis生成器自动生成实体类、DAO接口和Mapping映射文件 SSM框架搭建&#xff08;四&#xff09; sprin…

10 个有关 String 的面试问题

2019独角兽企业重金招聘Python工程师标准>>> 下面是面试中最容易问到的有关String的问题。 1. 如何比较两个字符串&#xff1f;使用“”还是equals()方法&#xff1f; 简单来讲&#xff0c;“”测试的是两个对象的引用是否相同&#xff0c;而equals()比较的是两个字…

基于ASP.NET Core 6.0的整洁架构

背景最近尝试录制了一个系列视频&#xff1a;《ASP.NET Core 6.0Vue.js 3 实战开发》&#xff0c;本节是视频内部整洁架构的理论和实战的文字稿。因为在录制之前&#xff0c;我通常会编写完整的文字内容作为视频文案&#xff0c;这里分享给大家&#xff0c;希望对你有所帮助。如…

大并发数据队列

图解#include<stdio.h>#include<stdlib.h>#include<memory.h>#define N 100#define mytype intstruct MyQueue{mytype data[N];//数组存储队列int front;//拉屎int rear;//吃东西};typedef struct MyQueue myQ;//初始化void init(myQ *p){p->front p->…

java单精度实型_Java的八种基本类型及其各种数据类型的相互转换

一、八种基本类型1、六种数字类型(四个整数型&#xff0c;两个浮点型)字节型byte 8位-2^7到2^7短整型short 16位整型int 32位 长整型long 64位单精度float 32位 双精度double 64位2、一种字符类型字符型char 8位3、还有一种布尔型。布尔型&#xff1a;boolean 8位 可存储&q…

geotrellis使用(二十九)迁移geotrellis至1.1.1版

目录 前言升级过程总结一、前言 由于忙着安装OpenStack等等各种事情&#xff0c;有半年的时间没有再亲密的接触geotrellis&#xff0c;甚至有半年的时间没能畅快的写代码。近来OpenStack折腾的稍见成效&#xff0c;历经九九八十一Failure后成功的在16台服务器上搭建了云平台&am…

python中的logger模块详细讲解

logger 提供了应用程序可以直接使用的接口handler将(logger创建的)日志记录发送到合适的目的输出filter提供了细度设备来决定输出哪条日志记录formatter决定日志记录的最终输出格式 logging模块介绍 Python的logging模块提供了通用的日志系统&#xff0c;熟练使用logging模块可…

总在用户态调试 C# 程序,终还是搭了一个内核态环境

一&#xff1a;背景 一直在用 WinDbg 调试用户态程序&#xff0c;并没有用它调试过 内核态&#xff0c;毕竟不是做驱动开发&#xff0c;也没有在分析 dump 中需要接触用内核态的需求&#xff0c;但未知的事情总觉得很酷&#xff0c;加上最近在看 《深入解析 Windows 操作系统》…

CentOS搭建Sqoop环境

Sqoop是一个用来将Hadoop&#xff08;Hive、HBase&#xff09;和关系型数据库中的数据相互转移的工具&#xff0c;可以将一个关系型数据库&#xff08;例如&#xff1a;MySQL ,Oracle ,Postgres等&#xff09;中的数据导入到Hadoop的HDFS中&#xff0c;也可以将HDFS的数据导入到…

bat kafka启动_windows下搭建Kafka,并通过命令窗口收发消息

参考网址&#xff1a;前提条件&#xff1a;windows环境需要安装jdk2.由于Kafka依赖于zookeeper&#xff0c;所以也需要下载zookeeper,可以通过官网下载http://zookeeper.apache.org/3.安装zookeeper将压缩包解压后&#xff0c;到bin目录下&#xff0c;启动zkServer.bat即可注意…