程序笔记   发布时间:2022-07-19  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了重新整理 .net core 实践篇—————HttpClientFactory[三十二]大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

简单整理一下httpClientFactory 。

正文

这个httpFactory 主要有下面的功能:

  1. 管理内部httpmessageHandler 的生命周期,灵活应对资源问题和DNS刷新问题

  2. 支持命名话、类型化配置,集中管理配置,避免冲突。

  3. 灵活的出站请求管道配置,轻松管理请求生命周期

  4. 内置管道最外层和最内层日志记录器,有information 和 Trace 输出

核心对象:

  1. httpClient

  2. httpmessageHandler

  3. SocketshttpHandler

  4. DelegaTingHandler

  5. IhttpClientFactory

  6. IhttpClientBuilder

请求的大概流程图为:

重新整理 .net core 实践篇—————HttpClientFactory[三十二]

从上图中看到SocketshttpHandler 才是正确去发起请求。

里面的logginScopehttpmessageHandler 、CustommessageHandler 还有LogginghttpmessageHandler,他们都是中间处理,处于管道之中,可以理解为中间件部分。

logginScopehttpmessageHandler 是没有经过CustommessageHandler 的日志,LogginghttpmessageHandler 是经过CustommessageHandler 的日志,也就是说LogginghttpmessageHandler 才是正在去发送请求的数据。

httpClientFactory 提供了三种创建httpClient的模式:

创建模式:

  1. 工厂模式

  2. 命名客户端模式

  3. 类型化客户端模式

那么就来看一下各种模式的不同吧。

工厂模式

在.net core 中使用工厂模式,要引入:

services.AddhttpClient();

然后使用:

public class orderserviceClient
{
	private IhttpClientFactory _httpClientFactory;

	public orderserviceClient(IhttpClientFactory httpClientFactory)
	{
		_httpClientFactory = httpClientFactory;
	}

	public async Task<String> Get()
	{
		var client = _httpClientFactory.CreateClient();

		return await client.GetStringAsync("http://localhost:9000/WeatherForecast");
	}
}

这样就是使用工厂模式了。

可能有些人认为IhttpClientFactory,可能它的实现是httpClientFactory,因为.net core的基础库中就有httpClientFactory,然而实际不是,那么来看下源码。

public static IserviceCollection AddhttpClient(
  this IserviceCollection services)
{
  if (services == null)
	throw new ArgumentNullException(nameof (services));
  services.AddLogging();
  services.Addoptions();
  services.TryAddTransient<httpmessageHandlerBuilder, DefaulthttpmessageHandlerBuilder>();
  services.TryAddSingleton<DefaulthttpClientFactory>();
  services.TryAddSingleton<IhttpClientFactory>((Func<IserviceProvider, IhttpClientFactory>) (serviceProvider => (IhttpClientFactory) serviceProvider.Getrequiredservice<DefaulthttpClientFactory>()));
  services.TryAddSingleton<IhttpmessageHandlerFactory>((Func<IserviceProvider, IhttpmessageHandlerFactory>) (serviceProvider => (IhttpmessageHandlerFactory) serviceProvider.Getrequiredservice<DefaulthttpClientFactory>()));
  services.TryAdd(serviceDescriptor.Transient(typeof (ITypedhttpClientFactory<>), typeof (DefaultTypedhttpClientFactory<>)));
  services.TryAdd(serviceDescriptor.Singleton(typeof (DefaultTypedhttpClientFactory<>.CachE), typeof (DefaultTypedhttpClientFactory<>.CachE)));
  services.TryAddEnumerable(serviceDescriptor.Singleton<IhttpmessageHandlerBuilderFilter, LogginghttpmessageHandlerBuilderFilter>());
  services.TryAddSingleton<httpClientMappingRegistry>(new httpClientMappingRegistry());
  return services;
}

其他暂且不看,IhttpClientFactory的实现类是DefaulthttpClientFactory;

看下CreateClient:

public httpClient CreateClient(String Name)
{
  if (name == null)
	throw new ArgumentNullException(nameof (Name));
  httpClient httpClient = new httpClient(this.CreateHandler(Name), falsE);
  httpClientFactoryOptions clientFactoryOptions = this._optionsMonitor.Get(Name);
  for (int index = 0; index < clientFactoryOptions.httpClientActions.Count; ++indeX)
	clientFactoryOptions.httpClientActions[index](httpClient);
  return httpClient;
}

public httpmessageHandler CreateHandler(String Name)
{
  if (name == null)
	throw new ArgumentNullException(nameof (Name));
  ActiveHandlerTrackingEntry entry = this._activeHandlers.GetOrAdd(name, this._entryFactory).Value;
  this.StartHandlerEntryTimer(entry);
  return (httpmessageHandler) entry.Handler;
}

这里我们似乎没有传递name,那么有可能是扩展类:

public static httpClient CreateClient(this IhttpClientFactory factory)
{
  if (factory == null)
	throw new ArgumentNullException(nameof (factory));
  return factory.CreateClient(Microsoft.Extensions.Options.Options.DefaultName);
}

测试一下:

注入服务:

 services.AddSingleton<orderserviceClient>();

测试代码:

[Route("api/[Controller]")]
public class OrderController : Controller
{
	orderserviceClient _orderserviceClient;

	public OrderController(orderserviceClient orderserviceClient)
	{
		_orderserviceClient = orderserviceClient;
	}

	[httpGet("Get")]
	public async Task<String> Get()
	{
		return await _orderserviceClient.Get();
	}
}

结果:

重新整理 .net core 实践篇—————HttpClientFactory[三十二]

这里说明一下,那个访问9000的端口,就是新建一个api项目,然后把端口改成9000的demo,这里就不演示了。

命令客户端方式

services.AddhttpClient();
services.AddhttpClient("NamedorderserviceClient", client =>
{
	client.DefaultrequestHeaders.Add("token","123456");
	client.baseAddress = new Uri("http://locahost:9000");
});

前面提及到client可以命名,那么这里就可以提前创建好对应的客户端配置。 AddhttpClient:

public static IhttpClientBuilder AddhttpClient(
  this IserviceCollection services,
  String name,
  Action<httpClient> configureClient)
{
  if (services == null)
	throw new ArgumentNullException(nameof (services));
  if (name == null)
	throw new ArgumentNullException(nameof (Name));
  if (configureClient == null)
	throw new ArgumentNullException(nameof (configureClient));
  services.AddhttpClient();
  DefaulthttpClientBuilder builder = new DefaulthttpClientBuilder(services, Name);
  builder.ConfigurehttpClient(configureClient);
  return (IhttpClientBuilder) builder;
}

看下这个ConfigurehttpClient:

public static IhttpClientBuilder ConfigurehttpClient(
  this IhttpClientBuilder builder,
  Action<httpClient> configureClient)
{
  if (builder == null)
	throw new ArgumentNullException(nameof (builder));
  if (configureClient == null)
	throw new ArgumentNullException(nameof (configureClient));
  builder.services.Configure<httpClientFactoryOptions>(builder.Name, (Action<httpClientFactoryOptions>) (options => options.httpClientActions.Add(configureClient)));
  return builder;
}

可以看到这里只是做了配置,其他什么也没干。那么什么时候用到的呢?

public httpClient CreateClient(String Name)
{
  if (name == null)
	throw new ArgumentNullException(nameof (Name));
  httpClient httpClient = new httpClient(this.CreateHandler(Name), falsE);
  httpClientFactoryOptions clientFactoryOptions = this._optionsMonitor.Get(Name);
  for (int index = 0; index < clientFactoryOptions.httpClientActions.Count; ++indeX)
	clientFactoryOptions.httpClientActions[index](httpClient);
  return httpClient;
}

httpClientFactoryOptions 眼熟吧。

client =>{
       client.DefaultrequestHeaders.Add("token","123456");
       client.baseAddress = new Uri("http://localhost:9000");
}

然后clientFactoryOptions.httpClientActionsindex;就会调用上面的这个Action。

那么我们使用的时候这么写:

public class NamedorderserviceClient
{
	private IhttpClientFactory _httpClientFactory;

	private const String _clientName = "NamedorderserviceClient";

	public NamedorderserviceClient(IhttpClientFactory httpClientFactory)
	{
		_httpClientFactory = httpClientFactory;
	}

	public async Task<String> Get()
	{
		var client = _httpClientFactory.CreateClient(_clientName);

		return await client.GetStringAsync("/WeatherForecast");
	}
}

测试一下: 注入服务:

services.AddSingleton<NamedorderserviceClient>();

测试代码:

[Route("api/[Controller]")]
public class OrderController : Controller
{
	NamedorderserviceClient _orderserviceClient;

	public OrderController(NamedorderserviceClient orderserviceClient)
	{
		_orderserviceClient = orderserviceClient;
	}

	[httpGet("Get")]
	public async Task<String> Get()
	{
		return await _orderserviceClient.Get();
	}
}

点一下:

重新整理 .net core 实践篇—————HttpClientFactory[三十二]

可以看到创建的httpclient 属性如上。

效果如下:

重新整理 .net core 实践篇—————HttpClientFactory[三十二]

其实在我们使用过程中最好去使用这种方式,有两个好处。

  1. 不同客户端可以单独配置

  2. 不同的可以的生命周期不同,即使一个httpclient崩溃了,另外一个httpclient也可以正常请求。

那么除了配置client的一些基本配置,如baseurl或者header这种。

services.AddhttpClient("NamedorderserviceClient", client =>
{
	client.DefaultrequestHeaders.Add("token","123456");
	client.baseAddress = new Uri("http://localhost:9000");
}).SetHandlerLifetime(TimeSpan.Fromminutes(20));

还可以设置dns时间。

当然最重要可以为每个httpclient自定义不同的管道,上文提及到到达正在的执行的过程中,会经过管道,中间我们@R_696_8311@。

public class requestCustomHandler: DelegaTingHandler
{
	protected override async Task<httpResponsemessage> SendAsync(httprequestmessage requestmessage, CancellationToken cancellationToken)
	{
		requestmessage.Headers.Add("token2",Guid.NewGuid().ToString());
                // 请求前处理
		var request = await base.SendAsync(requestmessage, cancellationToken);
                // 请求后处理
		return request;
	}
}

然后这样加入:

 services.AddSingleton<requestCustomHandler>();
services.AddhttpClient("NamedorderserviceClient", client =>
{
	client.DefaultrequestHeaders.Add("token","123456");
	client.baseAddress = new Uri("http://localhost:9000");
}).SetHandlerLifetime(TimeSpan.Fromminutes(20)).AddhttpmessageHandler(provider=>provider.Getservice<requestCustomHandler>());

那么来看下其管道怎么实现的:

    public static IhttpClientBuilder AddhttpmessageHandler(
      this IhttpClientBuilder builder,
      Func<IserviceProvider, DelegaTingHandler> configureHandler)
    {
      if (builder == null)
        throw new ArgumentNullException(nameof (builder));
      if (configureHandler == null)
        throw new ArgumentNullException(nameof (configureHandler));
      builder.services.Configure<httpClientFactoryOptions>(builder.Name, (Action<httpClientFactoryOptions>) (options => options.httpmessageHandlerBuilderActions.Add((Action<httpmessageHandlerBuilder>) (b => b.AdditionalHandlers.Add(configureHandler(b.services))))));
      return builder;
    }

其也就是做了一些配置,生成了一些action,那么哪里调用了呢?

在DefaulthttpClientFactory:

public DefaulthttpClientFactory(
  IserviceProvider services,
  IserviceScopeFactory scopeFactory,
  ILoggerFactory loggerFactory,
  IOptionsMonitor<httpClientFactoryOptions> optionsMonitor,
  IEnumerable<IhttpmessageHandlerBuilderFilter> filters)
{
  if (services == null)
	throw new ArgumentNullException(nameof (services));
  if (scopeFactory == null)
	throw new ArgumentNullException(nameof (scopeFactory));
  if (loggerFactory == null)
	throw new ArgumentNullException(nameof (loggerFactory));
  if (optionsMonitor == null)
	throw new ArgumentNullException(nameof (optionsMonitor));
  if (filters == null)
	throw new ArgumentNullException(nameof (filters));
  this._services = services;
  this._scopeFactory = scopeFactory;
  this._optionsMonitor = optionsMonitor;
  this._filters = filters.ToArray<IhttpmessageHandlerBuilderFilter>();
  this._logger = (ILogger) loggerFactory.CreateLogger<DefaulthttpClientFactory>();
  this._activeHandlers = new ConcurrentDictionary<String, Lazy<ActiveHandlerTrackingEntry>>((IEqualityComparer<String>) StringComparer.ordinal);
  this._entryFactory = (Func<String, Lazy<ActiveHandlerTrackingEntry>>) (name => new Lazy<ActiveHandlerTrackingEntry>((Func<ActiveHandlerTrackingEntry>) (() => this.CreateHandlerEntry(Name)), LazyThreadSafetymode.ExecutionAndPublication));
  this._expiredHandlers = new ConcurrentQueue<ExpiredHandlerTrackingEntry>();
  this._expiryCallBACk = new TimerCallBACk(this.ExpiryTimer_Tick);
  this._cleanupTimerLock = new object();
  this._cleanupActiveLock = new object();
}

看到这一行:

 this._entryFactory = (Func<String, Lazy<ActiveHandlerTrackingEntry>>) (name => new Lazy<ActiveHandlerTrackingEntry>((Func<ActiveHandlerTrackingEntry>) (() => this.CreateHandlerEntry(Name)), LazyThreadSafetymode.ExecutionAndPublication));

关注一下这个CreateHandlerEntry,这里调用了,后面会看到。

我们创建client的时候:

public httpClient CreateClient(String Name)
{
  if (name == null)
	throw new ArgumentNullException(nameof (Name));
  httpClient httpClient = new httpClient(this.CreateHandler(Name), falsE);
  httpClientFactoryOptions clientFactoryOptions = this._optionsMonitor.Get(Name);
  for (int index = 0; index < clientFactoryOptions.httpClientActions.Count; ++indeX)
	clientFactoryOptions.httpClientActions[index](httpClient);
  return httpClient;
}

public httpmessageHandler CreateHandler(String Name)
{
  if (name == null)
	throw new ArgumentNullException(nameof (Name));
  ActiveHandlerTrackingEntry entry = this._activeHandlers.GetOrAdd(name, this._entryFactory).Value;
  this.StartHandlerEntryTimer(entry);
  return (httpmessageHandler) entry.Handler;
}

在createHandler 调用_entryFactory:

那么来看一下CreateHandlerEntry:

internal ActiveHandlerTrackingEntry CreateHandlerEntry(String Name)
{
  IserviceProvider provider = this._services;
  IserviceScope scope = (IserviceScopE) null;
  httpClientFactoryOptions options = this._optionsMonitor.Get(Name);
  if (!options.SuppressHandlerScopE)
  {
	scope = this._scopeFactory.CreateScope();
	provider = scope.serviceProvider;
  }
  try
  {
	httpmessageHandlerBuilder requiredservice = provider.Getrequiredservice<httpmessageHandlerBuilder>();
	requiredservice.Name = name;
	Action<httpmessageHandlerBuilder> next = new Action<httpmessageHandlerBuilder>(ConfigurE);
	for (int index = this._filters.Length - 1; index >= 0; --indeX)
	  next = this._filters[index].Configure(next);
	next(requiredservicE);
	LifetimeTrackinghttpmessageHandler handler = new LifetimeTrackinghttpmessageHandler(requiredservice.build());
	return new ActiveHandlerTrackingEntry(name, handler, scope, options.HandlerLifetimE);
  }
  catch
  {
	scope?.Dispose();
	throw;
  }

  void Configure(httpmessageHandlerBuilder b)
  {
	for (int index = 0; index < options.httpmessageHandlerBuilderActions.Count; ++indeX)
	  options.httpmessageHandlerBuilderActions[index](b);
  }
}

在Configure 执行了我们前面执行的action,也就是b => b.AdditionalHandlers.Add(configureHandler(b.services))。

那么这个AdditionalHandlers有啥用?

这里简单说一下哈,详细会到细节篇中说明。

httpclient 执行请求,其实最后是httpmessageHandler去执行。那么这个httpmessageHandler 怎么来的呢?

public abstract class httpmessageHandlerBuilder
{
public abstract String Name { get; set; }

public abstract httpmessageHandler PriMaryHandler { get; set; }

public abstract IList<DelegaTingHandler> AdditionalHandlers { get; }

public virtual IserviceProvider services { get; }

public abstract httpmessageHandler Build();

protected internal static httpmessageHandler CreateHandlerPipeline(
  httpmessageHandler priMaryHandler,
  IEnumerable<DelegaTingHandler> additionalHandlers)
{
  if (priMaryHandler == null)
	throw new ArgumentNullException(nameof (priMaryHandler));
  if (additionalHandlers == null)
	throw new ArgumentNullException(nameof (additionalHandlers));
  IReadOnlyList<DelegaTingHandler> delegaTingHandlerList = (IReadOnlyList<DelegaTingHandler>) ((object) (additionalHandlers as IReadOnlyList<DelegaTingHandler>) ?? (object) additionalHandlers.ToArray<DelegaTingHandler>());
  httpmessageHandler httpmessageHandler = priMaryHandler;
  for (int index = delegaTingHandlerList.Count - 1; index >= 0; --indeX)
  {
	DelegaTingHandler delegaTingHandler = delegaTingHandlerList[index];
	if (delegaTingHandler == null)
	  throw new InvalidoperationException(resources.FormathttpmessageHandlerBuilder_AdditionalHandlerIsNull((object) nameof (additionalHandlers)));
	if (delegaTingHandler.InnerHandler != null)
	  throw new InvalidoperationException(resources.FormathttpmessageHandlerBuilder_AdditionHandlerIsInvalid((object) "InnerHandler", (object) "DelegaTingHandler", (object) nameof (httpmessageHandlerBuilder), (object) Environment.NewLine, (object) delegaTingHandler));
	delegaTingHandler.InnerHandler = httpmessageHandler;
	httpmessageHandler = (httpmessageHandler) delegaTingHandler;
  }
  return httpmessageHandler;
}
}

AdditionalHandlers 眼熟吧,这个httpmessageHandler 和中间件一样玩的都是套娃功能,形成一个小周天。

重新整理 .net core 实践篇—————HttpClientFactory[三十二]

类型化客户端模式

这个是什么呢?

public class TypeorderserviceClient
{
	private httpClient _httpClient;

	public TypeorderserviceClient(httpClient httpClientFactory)
	{
		_httpClient = httpClientFactory;
	}

	public async Task<String> Get()
	{

		return await _httpClient.GetStringAsync("http://localhost:9000/WeatherForecast");
	}
}

这种最为简单,直接生成了httpClient ,名字就是TypeorderserviceClient。

那么我们是否能够为其添加一些配置呢?可以的。

services.AddhttpClient<TypeorderserviceClient>( client =>
{
	client.DefaultrequestHeaders.Add("token","123456");
	client.baseAddress = new Uri("http://localhost:9000");
}).SetHandlerLifetime(TimeSpan.Fromminutes(20)).AddhttpmessageHandler(provider=>provider.Getservice<requestCustomHandler>());

这样就ok的,没有名字会使用泛型名。

public static IhttpClientBuilder AddhttpClient<TClient>(
  this IserviceCollection services,
  Action<httpClient> configureClient)
  where TClient : class
{
  if (services == null)
	throw new ArgumentNullException(nameof (services));
  if (configureClient == null)
	throw new ArgumentNullException(nameof (configureClient));
  services.AddhttpClient();
  String typeDisplayName = TypenameHelper.GetTypeDisplayName(typeof (TClient), false, false, true, '+');
  DefaulthttpClientBuilder builder = new DefaulthttpClientBuilder(services, typeDisplayName);
  builder.ConfigurehttpClient(configureClient);
  builder.AddTypedClientCore<TClient>(true);
  return (IhttpClientBuilder) builder;
}

以上只是个人整理,如有错误,望请指点。下一节grpc。

大佬总结

以上是大佬教程为你收集整理的重新整理 .net core 实践篇—————HttpClientFactory[三十二]全部内容,希望文章能够帮你解决重新整理 .net core 实践篇—————HttpClientFactory[三十二]所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。