In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
Editor to share with you how to use the HttpClientFactory class in the .NET Core, I believe most people do not know much about it, so share this article for your reference, I hope you can learn a lot after reading this article, let's learn about it!
I. use of HttpClient
In C #, if we need to send a Http request to a specific URL address, we usually use the HttpClient class. The HttpClient is declared and initialized inside the using, as in the following code:
Using (var httpClient = new HttpClient ()) {/ / logical processing code}
The HttpClient class contains many useful methods, and the above code can meet most of the requirements, but unexpected things can happen if you use it improperly.
The technical point of the above code: when you use an object that inherits the IDisposable interface, it is recommended to declare and initialize it in the using code block. When the using code snippet is executed, the object will be automatically released without the need for manual display Dispose operation.
The resources occupied by the object should be made sure to be released in a timely manner, but this is wrong for network connections. The specific reasons are as follows:
Network connection takes a certain amount of time, frequently open and close connections, the performance will be affected.
Opening a network connection consumes lower-level socket resources, but when HttpClient calls its own Dispose method, it does not immediately release that resource, which means that your program may cause unexpected exceptions due to running out of connection resources.
Take a look at the following code
Using System;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.Text;using System.Threading.Tasks;namespace HttpClientDemo {class Program {static void Main (string [] args) {/ / using (var httpClient = new HttpClient ()) / {/ logical processing code / /} HttpAsync () Console.WriteLine ("Hello World!"); Console.Read ();} public static async void HttpAsync () {for (int I = 0; I)
< 10; i++) { using (var client = new HttpClient()) { var result = await client.GetAsync("http://www.baidu.com"); Console.WriteLine($"{i}:{result.StatusCode}"); } } } }} 运行项目输出结果后,通过netstate查看下TCP连接情况,会发现连接依然存在,状态为"TIME_WAIT"(继续等待看是否还有延迟的包会传输过来)。 这里就会出现一个坑:在高并发的情况下,连接来不及释放,socket连接被耗尽,耗尽之后就会出现错误。就是会出现"各种套接字问题"。 那么如何解决这个问题呢?比较好的解决方法是延长HttpClient对象的使用寿命,实现HttpClient对象的复用,比如对其建一个静态的对象: private static HttpClient Client = new HttpClient(); 我们使用这种方式优化上面的代码 using System;using System.Net.Http;namespace HttpClientDemo{ class Program { private static readonly HttpClient _client = new HttpClient(); static void Main(string[] args) { HttpAsync(); Console.WriteLine("Hello World"); Console.ReadKey(); } public static async void HttpAsync() { for (int i = 0; i < 10; i++) { var result = await _client.GetAsync("http://www.baidu.com"); Console.WriteLine($"{i}:{result.StatusCode}"); } } }} 这样调整HttpClient的引用后,虽然可以解决一些问题,但是仍然存在一些问题: 因为是复用的HttpClient,那么一些公共的设置就没办法灵活的调整,如请求头的自定义。 因为HttpClient请求每个url时,会缓存url对应的主机ip,从而会导致DNS更新失效。 为了解决这些问题,在.NET Core 2.1中引入了新的HttpClientFactory类。 二、HttpClientFactory使用 微软在.NET Core 2.1中新引入了HttpClientFactory类,具有如下的优势: HttpClientFactory很高效,可以最大程度上节省系统的sock而。 Factory,顾名思义HttpClientfactory就是HttpClient的工厂,内部已经帮我们处理好了对HttpClient的管理,不需要我们人工进行对象释放,同时,支持自定义请求头、支持DNS更新等。 我们用一个ASP.NET Core的程序作为示例,它的用法非常简单,首先是对其进行IOC注册: public void ConfigureServices(IServiceCollection services){ // 注入HttpClient services.AddHttpClient("client_1", config =>/ / the name=client_1 specified here can facilitate us to take this example later {config.BaseAddress = new Uri ("http://www.baidu.com"); config.DefaultRequestHeaders.Add (" header_1 "," header_1 ");}); services.AddHttpClient (" client_2 ", config = > {config.BaseAddress = new Uri (" https://www.qq.com/"); ") Config.DefaultRequestHeaders.Add ("header_2", "header_2");}); services.AddHttpClient (); services.AddControllers ();}
Then create a HttpClient object in the controller through IHttpClientFactory, and the operation is the same as before, but there is no need to worry about the release of its internal resources:
Using System.Net.Http;using System.Threading.Tasks;using Microsoft.AspNetCore.Mvc;namespace HttpClientFactoryDemo.Controllers {[Route ("api/ [controller]")] [ApiController] public class DemoController: ControllerBase {IHttpClientFactory _ httpClientFactory; / public DemoController (IHttpClientFactory httpClientFactory) {_ httpClientFactory = httpClientFactory through constructor } public async Task Get () {var client = _ httpClientFactory.CreateClient ("client_1"); / / reuse the httpclient var result of client_1 defined in Startup = await client.GetStringAsync ("/ page1.html"); var client2 = _ httpClientFactory.CreateClient (); / / create a new HttpClient var result2 = await client.GetAsync ("http://www.baidu.com");") Return result2.StatusCode.ToString ();}
The result of running the program:
Source code of AddHttpClient:
Public static IServiceCollection AddHttpClient (this IServiceCollection services) {if (services = = null) {throw new ArgumentNullException (nameof (services));} services.AddLogging (); services.AddOptions (); / Core abstractions / / services.TryAddTransient (); services.TryAddSingleton (); / Typed Clients / / services.TryAdd (ServiceDescriptor.Singleton (typeof (ITypedHttpClientFactory), typeof (DefaultTypedHttpClientFactory)) / Misc infrastructure / / services.TryAddEnumerable (ServiceDescriptor.Singleton ()); return services;}
Look at the following code:
Services.TryAddSingleton ()
Here the DefaultHttpClientFactory class is bound for the IHttpClientFactory interface when dependency injection is added.
Let's look at the key CreateClient methods in the IHttpClientFactory interface:
Public HttpClient CreateClient (string name) {if (name = = null) {throw new ArgumentNullException (nameof (name));} var entry = _ activeHandlers.GetOrAdd (name, _ entryFactory) .value; var client = new HttpClient (entry.Handler, disposeHandler: false); StartHandlerEntryTimer (entry); var options = _ optionsMonitor.Get (name); for (var I = 0; I
< options.HttpClientActions.Count; i++) { options.HttpClientActions[i](client); } return client;} 从代码中我们可以看出:HttpClient的创建不在是简单的new HttpClient(),而是传入了两个参数:HttpMessageHandler handler与bool disposeHandler。 disposeHandler参数为false时表示要重用内部的handler对象。handler参数则从上一句的代码中可以看出是以name为键值从一字典中取出,又因为DefaultHttpClientFactory类是通过TryAddSingleton方法注册的,也就意味着其为单例,那么这个内部字典便是唯一的,每个键值对应的ActiveHandlerTrackingEntry对象也是唯一,该对象内部中包含着handler。 下一句代码StartHandlerEntryTimer(entry); 开启了ActiveHandlerTrackingEntry对象的过期计时处理。默认过期时间是2分钟。 internal void ExpiryTimer_Tick(object state){ var active = (ActiveHandlerTrackingEntry)state; // The timer callback should be the only one removing from the active collection. If we can't find // our entry in the collection, then this is a bug. var removed = _activeHandlers.TryRemove(active.Name, out var found); Debug.Assert(removed, "Entry not found. We should always be able to remove the entry"); Debug.Assert(object.ReferenceEquals(active, found.Value), "Different entry found. The entry should not have been replaced"); // At this point the handler is no longer 'active' and will not be handed out to any new clients. // However we haven't dropped our strong reference to the handler, so we can't yet determine if // there are still any other outstanding references (we know there is at least one). // // We use a different state object to track expired handlers. This allows any other thread that acquired // the 'active' entry to use it without safety problems. var expired = new ExpiredHandlerTrackingEntry(active); _expiredHandlers.Enqueue(expired); Log.HandlerExpired(_logger, active.Name, active.Lifetime); StartCleanupTimer();} 先是将ActiveHandlerTrackingEntry对象传入新的ExpiredHandlerTrackingEntry对象。 public ExpiredHandlerTrackingEntry(ActiveHandlerTrackingEntry other){ Name = other.Name; _livenessTracker = new WeakReference(other.Handler); InnerHandler = other.Handler.InnerHandler;} 在其构造方法内部,handler对象通过弱引用方式关联着,不会影响其被GC释放。 然后新建的ExpiredHandlerTrackingEntry对象被放入专用的队列。 最后开始清理工作,定时器的时间间隔设定为每10秒一次。 internal void CleanupTimer_Tick(object state){ // Stop any pending timers, we'll restart the timer if there's anything left to process after cleanup. // // With the scheme we're using it's possible we could end up with some redundant cleanup operations. // This is expected and fine. // // An alternative would be to take a lock during the whole cleanup process. This isn't ideal because it // would result in threads executing ExpiryTimer_Tick as they would need to block on cleanup to figure out // whether we need to start the timer. StopCleanupTimer(); try { if (!Monitor.TryEnter(_cleanupActiveLock)) { // We don't want to run a concurrent cleanup cycle. This can happen if the cleanup cycle takes // a long time for some reason. Since we're running user code inside Dispose, it's definitely // possible. // // If we end up in that position, just make sure the timer gets started again. It should be cheap // to run a 'no-op' cleanup. StartCleanupTimer(); return; } var initialCount = _expiredHandlers.Count; Log.CleanupCycleStart(_logger, initialCount); var stopwatch = ValueStopwatch.StartNew(); var disposedCount = 0; for (var i = 0; i < initialCount; i++) { // Since we're the only one removing from _expired, TryDequeue must always succeed. _expiredHandlers.TryDequeue(out var entry); Debug.Assert(entry != null, "Entry was null, we should always get an entry back from TryDequeue"); if (entry.CanDispose) { try { entry.InnerHandler.Dispose(); disposedCount++; } catch (Exception ex) { Log.CleanupItemFailed(_logger, entry.Name, ex); } } else { // If the entry is still live, put it back in the queue so we can process it // during the next cleanup cycle. _expiredHandlers.Enqueue(entry); } } Log.CleanupCycleEnd(_logger, stopwatch.GetElapsedTime(), disposedCount, _expiredHandlers.Count); } finally { Monitor.Exit(_cleanupActiveLock); } // We didn't totally empty the cleanup queue, try again later. if (_expiredHandlers.Count >0) {StartCleanupTimer ();}}
The core of the above method is to determine whether the handler object has been GC, and if so, release its internal resources, that is, the network connection.
Going back to the code that originally created the HttpClient, you will find that no name parameter values have been passed in. This is thanks to the extension method of the HttpClientFactoryExtensions class.
Public static HttpClient CreateClient (this IHttpClientFactory factory) {if (factory = = null) {throw new ArgumentNullException (nameof (factory));} return factory.CreateClient (Options.DefaultName);}
The value of Options.DefaultName is string.Empty.
These are all the contents of the article "how to use HttpClientFactory classes in .NET Core". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.