大佬教程收集整理的这篇文章主要介绍了重新整理 .net core 实践篇—————HttpClientFactory[三十二],大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
这个httpFactory 主要有下面的功能:
支持命名话、类型化配置,集中管理配置,避免冲突。
灵活的出站请求管道配置,轻松管理请求生命周期
内置管道最外层和最内层日志记录器,有information 和 Trace 输出
核心对象:
请求的大概流程图为:
从上图中看到SocketshttpHandler 才是正确去发起请求。
里面的logginScopehttpmessageHandler 、CustommessageHandler 还有LogginghttpmessageHandler,他们都是中间处理,处于管道之中,可以理解为中间件部分。
logginScopehttpmessageHandler 是没有经过CustommessageHandler 的日志,LogginghttpmessageHandler 是经过CustommessageHandler 的日志,也就是说LogginghttpmessageHandler 才是正在去发送请求的数据。
httpClientFactory 提供了三种创建httpClient的模式:
创建模式:
工厂模式
命名客户端模式
类型化客户端模式
那么就来看一下各种模式的不同吧。
在.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();
}
}
结果:
这里说明一下,那个访问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();
}
}
断点一下:
可以看到创建的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 和中间件一样玩的都是套娃功能,形成一个小周天。
这个是什么呢?
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,请注明来意。