Erhan Ballıeker

Asp.Net Core API Backend ve Xamarin.Forms İle Kelime Oyunu Bölüm 3 (Asp.Net Core IHostedService ve BackgroundService ile Background Tasks)

Selamlar,

Bu yazım sizlere Asp.Net Core projenizde Background service yazmanın yöntemlerinden ve bizim yazdığımız oyun için bunu nasıl kullandığımızdan bahsetmek isiyorum.

Öncelikle bizim nasıl kullandığımızdan önce bir asp.net core projenizde arka planda belli zaman aralıklarında bir şeyler yapmak istediğiniz de neler yapabilirsiniz buna bakalım.

Bunun için başvuracağınız ilk arkadaş IHostedService.

Microsoft.Extensions.Hosting paketinde olan ve zaten Microsoft.AspNetCore.App metapackage ı ile elimize çoktan geçmiş olan bu arkadaş iki metot içeren bir interface

namespace Microsoft.Extensions.Hosting
{
    //
    // Summary:
    //     Defines methods for objects that are managed by the host.
    public interface IHostedService
    {
        //
        // Summary:
        //     Triggered when the application host is ready to start the service.
        //
        // Parameters:
        //   cancellationToken:
        //     Indicates that the start process has been aborted.
        Task StartAsync(CancellationToken cancellationToken);
        //
        // Summary:
        //     Triggered when the application host is performing a graceful shutdown.
        //
        // Parameters:
        //   cancellationToken:
        //     Indicates that the shutdown process should no longer be graceful.
        Task StopAsync(CancellationToken cancellationToken);
    }
}

StartAsync ve StopAsync bu kadar.

WebHostBuilder (WebHost – IWebHostBuilder) ile uygulamanız ayağa kalkıyor ise bu StartAsync metodu server ayağa kalktıktan hemen sonra (IApplicationLifetime.ApplicationStarted tetiklendikten sonra) tetikleniyor.

GenericHost (HostBuilder) ile uygulamanızı host ediyor iseniz bu sefer de IApplicationLifetime.ApplicationStarted metodu tetiklenmeden hemen önce bu StartAsync metodumuz tetikleniyor.

Aynı şekilde StopAsync de host shutdown olduktan sonra tetikleniyor. Burada IDisposable interface ini implemente etmek ve içeride kullandığımız diğer sınıfları da (ör: Timer gibi) burada dispose etmekte fayda var.

Örnek bir kullanım görelim.

internal class TimedHostedService : IHostedService, IDisposable
{
    private readonly ILogger _logger;
    private Timer _timer;

    public TimedHostedService(ILogger logger)
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is starting.");

        _timer = new Timer(DoWork, null, TimeSpan.Zero, 
            TimeSpan.FromSeconds(5));

        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        _logger.LogInformation("Timed Background Service is working.");
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is stopping.");

        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}

Yukarıda bir timer vasıtası ile 5 sn de bir log atıcak basit bir IHostedService implementastonu görüyoruz. shutdown olduğunda timer infinite state e alınıp dispose implementasyonunda da dispose ediliyor.

Peki bunun dışında muhemelen ihtiyacımız olacak olan bir detaydan bahsetmek isterim. Bir IHostedService imizde uygulamamıza Scoped lifetime ına sahip olarak register etmiş olduğumuz bir service kullanmak istersek, bunun için scope default olarak üretilmiyor, bunu bizim manuel yapmamız gerekiyor. Aşağıdaki gibi..

 private void DoWork()
    {
        _logger.LogInformation(
            "Consume Scoped Service Hosted Service is working.");

        using (var scope = Services.CreateScope())
        {
            var scopedProcessingService = 
                scope.ServiceProvider
                    .GetRequiredService();

            scopedProcessingService.DoWork();
        }
    }

Asp.Net Core 2.0 ile gelen IHostedService in 2.1 de daha kolay kullanımı için bir arkadaş daha var. Bu da BackgroundService, IHostedService implementasyonunu baştan sona yapmak istemezseniz bu arkadaşı Asp.Net Core 2.1 den sonra kullanmaya çekinmeyiniz, birçok senaryoda işinizi görecektir. Bu sayede sadece ExecuteAsync metodunu doldurmanız yeterli. Default olarak CancellationToken timeout süresi 5 sn. ama bu süreyide değiştirmemiz mümkün.

public class MyBackgroundService: BackgroundService
{
    private readonly ILogger _logger;

    public MyBackgroundService(ILogger logger)
    {
        ...
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogDebug($"Service is starting.");

        stoppingToken.Register(() =>
            _logger.LogDebug($"background task is stopping."));

        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogDebug($"task doing background work.");

            await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
        }

        _logger.LogDebug($"background task is stopping.");
    }

    ...
}

Cancellation token time out süresini aşağıdaki gibi değiştirebilirsiniz.

WebHost.CreateDefaultBuilder(args) .UseShutdownTimeout(TimeSpan.FromSeconds(10));

Peki biz bu uygulamada bu background service i ne için ve nasıl kullandık kısmına gelirsek. Bizim asıl ihtiyacımız olan şey, belli bir sürede birşeyler çalıştırmanın yanında o belli bir sürenin kendisi idi 🙂 Her bir oyun için 60 yada 90 saniyelik sürelerimiz var. Ve bu süreleri HostedService mizde sürekli olarak geri sayıyoruz. oyun bitikten sonra bazı işlemler için 30 saniyelik te bir bekleme süresi var. Yani bir kullanıcı oyunu açtığında ve canlı oyuna katılmak istediğinde, o an oyun oynanıyor ise oyunun bitmesine kaç saniye kaldığını yada oyun bitmiş ve 30 sn lik bekleme süresinde isek de yeni oyunun başlamasına kaç saniye kaldığını göstermemiz gerekti.

Aşağıdaki resimde ki gibi.

WhatsApp Image 2019-05-31 at 22.10.46 (1)

Kullanıcı oyuna katılmak istediğinde ona hosted service in o an geri sayarken ki saniyenisi dönüyoruz, signalr ile. Ve kalan süreyi bir kere client aldıktan sonra artık kendi cihazındaki timer ile sayma işlemi devam ediyor.

Bizim HostedService miz şu şekilde.

 internal class TimedHostedService : IHostedService, IDisposable
    {
        private readonly ILogger _logger;
        private readonly IHubContext _hubContext;
        public IServiceProvider Services { get; }
        private Timer _timer;

        public TimedHostedService(ILogger logger, IServiceProvider services, IHubContext hubContext)
        {
            _logger = logger;
            _hubContext = hubContext;
            Services = services;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Timed Background Service is starting.");

            _timer = new Timer(GetNewGameAndBroadcast, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));

            return Task.CompletedTask;
        }

        private async void GetNewGameAndBroadcast(object state)
        {
            if (TimerService.WaitDuration == 25)
            {
                _logger.LogInformation("Timed Background Service is working.");
                using (var scope = Services.CreateScope())
                {
                    var gameService = scope.ServiceProvider.GetRequiredService();
                    var game = await gameService.NewGame(4);
                    var gameJson = JsonConvert.SerializeObject(game);
                    TimerService.gameJson = gameJson;
                    try
                    {
                        await _hubContext.Clients.All.SendAsync("GameReceived", gameJson);
                    }
                    catch (Exception ex)
                    {
                         ...
                    }
                }
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Timed Background Service is stopping.");

            _timer?.Change(Timeout.Infinite, 0);

            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }

Burada her bir saniyede ilgili metodu çalıştırıp eğer oyun bekleme süresinde ise zaten online olan kullanıcılar için yeni oyunu 25 saniye kala üretip IHubContext i kullanarak tüm kullanıcılara broadcast ediyoruz, bu durumda her online olan kişi süre bittiğinde aynı oyuna başlıyor, oyun sırasında dahil olanlarda bu oluşturulmuş olan oyunu alıp kalan saniyeden oyuna devam ediyorlar.

Bu yazımda bahsetmek istediklerim bu kadar. Bir sonraki yazımda asıl oyun için gerekli olan Grid i nasıl oluşturduk neler yaptık bundan bahsedeceğim.

Bir sonraki yazımda görüşmek üzere.

Tüm .Net Client Projelerimizde Ortak Kullanabileceğimiz Bir API Client yazalım Bölüm 2

Selamlar,

Bir önceki yazımda hazırlığını yapmış olduğumuz projemizin asıl kısmına gelelim.

Projemize ApiClient adında bir sınıf ekliyorum ve içeriğini aşağıdaki gibi dolduruyorum. Bir göz gezdirip detaylarına değinelim.


 public class ApiClient
        {
            #region fields

            private static ApiClient shared;
            private static object obj = new object();

            #endregion

            #region properties

            internal static IServiceCollection Services { get; set; }
            internal static IServiceProvider ServiceProvider { get; private set; }
            private static ApiClient Shared
            {
                get
                {
                    if (shared == null)
                    {
                        lock (obj)
                        {
                            if (shared == null)
                            {
                                shared = new ApiClient();
                            }
                        }
                    }

                    return shared;
                }
            }

            #endregion

            #region api spesific properties

            private IRandomUserApi _IRandomUserApi { get => ServiceProvider.GetRequiredService(); }

            //Exposed public static props via ApiClient.Shared 
            public static IRandomUserApi RandomUserApi{ get => Shared._IRandomUserApi; }

            #endregion

            #region ctor

            private ApiClient()
            {
                if (Services == null)
                    Services = new ServiceCollection();

                Init();
            }

            #endregion

            #region internal methods

            private void Init()
            {
                ConfigureServices(Services);
                ServiceProvider = Services.BuildServiceProvider();
            }

            private void ConfigureServices(IServiceCollection services)
            {
                services.AddTransient<ITokenService, TokenService>();

                #region AnonymousApi Configuration

                services.AddRefitClient()
                .ConfigureHttpClient(c =>
                {
                    c.BaseAddress = new Uri("http://wordy.azurewebsites.net/");
                    c.Timeout = TimeSpan.FromSeconds(10);
                })
                .AddTransientHttpErrorPolicy(p => p.RetryAsync())
                .AddHttpMessageHandler(serviceProvider =>
                {
                    var token = serviceProvider.GetRequiredService().GetToken();
                    return new AuthTokenHandler(token);
                });

                #endregion

            }

            #endregion
        }

Yukarıdakilerle alakalı olarak şurayı da okumanızı öneririm.

Burada önceki yazımda projeme eklediğim kütüphaneleri kullanmaya başlıyorum artık.

Yukarıda dönen hikaye şu;

  • Singleton bir ApiClient objem var. Tüm refit interfacelerini birer property üzerinden dışarıya expose ediyorum.
  • ConfigureServices metodunu neredeyse Asp.Net Core daki gibi birebir aynı yapmaya çalıştım. IoC conteiner ı oluşturup, refit interfacelerimi ve polly policy lerimi ilgili enpoint e register ediyor
  • Son olarak aynı şekilde yazmış olduğum DelegatingHandler larıda client ıma ekliyorum ve herşey kullanıma hazır hale geliyor.

Bundan sonra herhangi bir clien projesinde şunu yapabilirim

ApiClient.RandomUserApi.GetUser(“3”);

dediğimde Client projem her ne olursa olsun ister bir core web app ister xamarin ister başka birşey,  httpclient factory üzerinden refit, polly, delegating handler lar da kullanarak güzel yapı kurmuş oluyorum.

Bunları istediğimiz gibi şekillendirip güncelleyip, istediğimiz gibi konfigüre edebiliriz ve hiçbir client projemize dokunmamış oluruz.

Ek olarak burada ITokenService ve TokenService diye bir service yazdım. Bunu da şuna örnek olarak kullanabiliriz. Örneğin kullanıcı mobil uygulama da login olduğunda aldığımız token Xamarin.Essentials s Preference paketi ile saklayarak AuthTokenDelegating handler a parametre olarak verebilmenin örneği olsun diye koydum.

Farkli client projeler de bu ITokenService implemenatasyonlarını ayrı yazarak başka türlü yerlerde saklayıp okuyabiliriz.

Xamarin projesi için bu işimizi görecektir.

   public interface ITokenService
    {
        string GetToken();
    }

Xamarin forms projesi için implementasyon;

    public class TokenService : ITokenService
    {
        public string GetToken()
        {
            if (Preferences.ContainsKey(Constants.AuthToken))
                return Preferences.Get(Constants.AuthToken, string.Empty);
            else
                return string.Empty;
        }
    }

Bunu muhtemelen bu ApiClient projesinde değil de client projelerde register etmek daha doğru olacaktır gerçek hayat senaryolarında.

Bir sonraki yazımda görüşmek üzere.

Asp.Net Core DelegatingHandler

Selamlar,

Son yazılarımda Asp.Net Core 2.1 ile gelen HttpClientFactory ve bu api ın çeşitli kütüphaneler ile kullanımından bahsetmiştim.

Bu yazımda da yine clientfactory ile alakalı olarak DeletagingHandler dan bahsetmek istiyorum. Daha önce de çeşitli yollarla muhtemelen yapmış olduğunuz bir şeyi delegatinghandler lar ile nasıl daha kolay yapabiliriz bunu görücez.

DelegatingHandler için ilk söyleyeceğim şey Asp.Net Core daki Middleware yapısına olan benzerliğidir. Middleware yapısında nasıl ki browserdan server mıza kadar gelen request in yaşam döngüsü içerisinde, Kestret ayağa kalktıktan sonra, request in geçeceği middleware leri ayarlıyorsak, burada da aynı şekilde, HttpClientFactory ile request attığımız client ımız için, gidecek olan request in arasına istediğimiz şekilde ve farklı delegatinghandler la ile girip, request i yarı da kesmekten tutun da, response ı tamamen değiştirmeye kadar herşeyi yapabiliyoruz.

Aşağıda basit bir DelegatingHandler örneğine bakalım.

   public class AuthTokenHandler : DelegatingHandler
    {
        private readonly string _token;
        private const string TOKEN = "Authorization";
        public AuthTokenHandler(string token)
        {
            _token = token;
        }

        protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!request.Headers.Contains(TOKEN) && string.IsNullOrEmpty(_token))
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest)
                {
                    Content = new StringContent("Missing auth token.")
                };
            }
            else if (!request.Headers.Contains(TOKEN) && !string.IsNullOrEmpty(_token))
            {
                request.Headers.Add(TOKEN, $"Bearer {_token}");
            }

            var response = await base.SendAsync(request, cancellationToken);

            return response;
        }
    }

Yukarıda yazılan DelegatingHandler ı inceleyelim.

  • Yazdığımız DelegatingHandler ımız  DelegatingHandler sınıfından türemek durumunda
  • protected override async Task SendAsync metodunun override ederek istediğimiz işlemleri request in öncesi ve sonrasında yapabiliyoruz.
  • Bu metod içerisinde await base.SendAsync(request, cancellationToken); 
    • kodundan önceki kodlar request üzerinde yapacağımız işlemler
    • sonraki kodlar ise gelen response üzerinde yapmak isteyeceğimiz kısımlardır.
  • Bu handler ımız gelen request içerisinde Authorization isminde bir header arıyor. Eğer bu handler a paramere olarak geçilmiş olan token değerini bulamaz ise geriye request i bir sonraki aşamaya bile göndermeden(tıpkı middleware lardki shorcut yapısı gibi) geriye doğrudan response u dönüyor.
  • Eğer handler a geçilen parametre boş değilse bir token değeri varsa bu token değerini, Authorization header içerisine Bearer token olarak koyuyor.

Bu şekilde bir api ile haberleşirken sıkça karşılaşığımız gibi token based authantication yönetimi için her bir request ile header da token yollama işini çok basit şekilde halledebilmiş oluyoruz.

Peki bu yazmış olduğumuz Delegating handler ı request in pipeline ına nasıl ekleyeceğiz buna bakalım.

   public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddTransient<AuthTokenHandler>();
            services.AddHttpClient("randomuserclient", client =>
            {
                client.BaseAddress = new Uri("https://randomuser.me/api");
                client.DefaultRequestHeaders.Add("Accept", "application/json");
                client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting");
            })
            .AddHttpMessageHandler<AuthTokenHandler>();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

Geçtiğimiz yazılarımızda HttpClientFactory ve bunun asp.net core projelerinde kullanımına değindiğim için fazla detaya girmeyeceğim bu tarafta.

Buradan httpclientfactory ile ilgili yazdığım yazıların detaylarına ulaşabilirsiniz

Burada basitçe bir namedclient kullanıp services e randomuserclient adında bir client ekliyoruz, ve en son noktada, clientfactory ile ilgili tüm ayarlamalarımızı yaptıktan sonra

.AddHttpMessageHandler<AuthTokenHandler>();

diyerek request pipeline ına bu handler ı eklemiş oluyoruz.

Bir dikkat etmemiz gereken noktada şu;

AddHttpMessageHandler demeden önce bu handler ı ekleyebilmek için, handler ın kendisini de önceden asp.net core dependency injection mekanizmasına tanıtmamız gerekiyor, bunun için de;

services.AddTransient<AuthTokenHandler>();

diyerek, container a bu sınıfımızı da ekliyoruz.

Aşağıda da her zaman olduğu client factory mizi kullanabiliriz. Burada önceki kullanım şekillerine göre hiçbir fark yok (NamedClient olarak eklediğimiz için yine CreateClient derken randomuserclient şeklinde ismini vererek çağırıyoruz.)

public class HomeController : Controller
    {
        private readonly IHttpClientFactory _httpClientFactory;
        public HomeController(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        public async Task Index()
        {
            var client = _httpClientFactory.CreateClient("randomuserclient");
            var result = await client.GetStringAsync("?results=5");
            return View();
        }

Bir sonraki yazımda birden fazla DelegatingHandler eklediğimiz durumlarda işler nasıl oluyor buna bakacağız.

Bir sonraki yazımda görüşmek üzere.

Asp.Net Core HttpClientFactory + Polly

Selamlar,

HttpClientFactory ile ilgili yazılarıma bir yenisini de Polly ile kullanımını inceleyerek devam etmek istedim.

Polly, açıkçası farkında olmasanız bile kullanmak zorunda olduğunuz bir kütüphane diyecek kadar ileri gidebilirim =) eğer ki benzer işlevleri zaten zamanında siz kendiniz farklı kütüphaneler olarak yazmadıysanız.

Polly ile ilgili yazılarıma şuradan ulaşabilirsiniz;

Polly sizelere network haberleşmesi sırasında uygulamanızda olması gereken bir çok pattern i extension metodlar halinde sunan ve çok daha fazlasını yapmanıza olanak tanıyan bir kütüphane.

Polly ile ;

  • Error ve Result Handling
  • Retry, WaitAndRetry, ForeverRetry gibi senarto ve patternleri
  • Circuit Breaker ve Advanced Circuit Breaker gibi senaryo ve patternleri
  • Cache, Bulkhead Isolation gibi

birçok şeyi çok basit şekilde uygulamalarınıza implemente edebiliyorsunuz.

Detaylarına yukarıda ki yazılarımdan ve kendi github reposundan ulaşabilirsiniz.

Asp.Net Core projelerimizde HttpClientFactory ile beraber kullanmak için yapacağımız ilk şey nugetten aşağıdaki paketi indirmek

Microsoft.Extensions.Http.Polly

Daha sonrasında kullanım şekli önceki yöntemler ile çok benzer oluyor.

Uygulamanın startup dosyasında aşağıdaki gibi AddHttpHandler dedikten sonra o Client ile ilgili uygulamak istediğiniz policy leri fluent bir şekilde ekleyebiliyorsunuz.

  public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddHttpClient("randomuserclient")
                .AddPolicyHandler(Policy.TimeoutAsync(TimeSpan.FromSeconds(10)))
                .AddTransientHttpErrorPolicy(p => p.RetryAsync(3));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

Yukarıdaki örneğimizde projemize NamedClient olarak eklemiş olduğumuz bir Client ın Polly yi nasıl kullanabileceğini söylemiş olduk.

Nuget ten indirdiğimiz paket ile beraber bize birçok extension metod geliyor.

Bunlardan sadece iki tanesini yukarıdaki senaryoda kullanmış olduk.

  • Time out policy ekleyerek, gidecek requestlerin en fazla 10 sn bekledikten sonra eğer response alamazsa doğrudan polly tarafından hataya düşürülmesini sağladık
  • Haberleşme sırasında geçici sorunlar olduğunda 3 kere yeniden denenmesini istedik. Anlık internet kayıpları, server anlık olarak response verememesi available olamaması gibi durumlarda request 3 kere yeniden denenecek.(RetryPattern)

Kullanım şekline bakığımızda ise öncekilerden hiçbir farkı yok. NamedClient eklemiş olduğumuz için aşağıdaki şekilde kullandık. Typed veya Basic usage olarak ekleseydik de o şekilde kullanacaktık. Polly nin yazarken ki dikkat etmemiz gereken tek kısmı Startup tarafında Policyleri Clientlarımıza implemente ettiğimiz kısım.

 public class HomeController : Controller
    {
        private readonly IHttpClientFactory_randomUserApi;
        public HomeController(IHttpClientFactory randomUserApi)
        {
            _randomUserApi = randomUserApi;
        }

        public async Task Index()
        {
            var result = await IHttpClientFactory.CreateClient("randomuserclient").GetUser("10");
            return View();
        }

      ..........

Bir önceki yazımda Refit ile HttpClientFactory i nasıl kullanırız bundan bahsetmiştim. Bir de bu üçünü yani Refit + Polly + HttpClientFactory i nasıl kullanırız buna bakalım.

Aşağıda basit bir Refit interface i görüyoruz.

namespace HttpClientFactoryRefit
{
    [Headers("Content-Type : application-json")]
    public interface IRandomUserApi
    {
        [Get("/")]
        Task GetUser(string results);
    }
}

Nugetten aynı projemize ;

Refit.HttpClientFactory 

paketini de indirdikten sonra aşağıdaki şekilde hem refit i AddRefitClient diyerek services a ekleyebilir, hem de sonrasında yukarıda ki gibi Polly Policy lerini aynı Client ın devamında ekleyebiliriz.

  public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddRefitClient()
                .ConfigureHttpClient(client =>
                {
                    client.BaseAddress = new Uri("https://randomuser.me/api");
                })
                .AddPolicyHandler(Policy.TimeoutAsync(TimeSpan.FromSeconds(10))) 
                .AddTransientHttpErrorPolicy(p => p.RetryAsync(3)); ;

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

Kullanım şeklinde de Refit ile HttpClientFactory i nasıl kullanıyor isek aynı şekilde kullanmaya devam ediyoruz.

public class HomeController : Controller
    {
        private readonly IRandomUserApi _randomUserApi;
        public HomeController(IRandomUserApi randomUserApi)
        {
            _randomUserApi = randomUserApi;
        }

        public async Task Index()
        {
            var result = await _randomUserApi.GetUser("10");
            return View();
        }
       

          .........

Bu yazımızda da hem Polly ile HttpClienFactory nasıl kullanacağımızı görmüş olduk hem de bir önceki yazımızda Refit ile kullandığımız gibi refit i de işin içine dahil ettik.

Refit + Polly + HttpClienFactory üçlüsü güçlü bir Api haberleşme altyapısı için çok büyük artılar sağlayacaktır projenize.

Bir sonraki yazımda görürşmek üzere.

Asp.Net Core HttpClientFactory + Refit

Selamlar,

Son birkaç yazıdır üzerinde durduğum HttpClientFactory ile sevdiğim ve sıkça kullandığımı kütüphanelerden biri olan Refit in beraber kullanımı hakkında yazmak istedim.

Daha önceki konu ile alakalı yazılarıma aşağıdaki linklerden ulaşabilirsiniz.

 

Refit daha önce kullanmadıysanız hemen kullanmaya başlamanız için çok sebep var. Detaylarına girmeyeğim, önceki yazılarda değinmiştim, ama özetle söyleyebilirimki bir enpoint ile yapacağınız tüm haberleşmeyi tek bir interface üzerinde hazırlayıp geri kalanını refit e bırakıyorsunuz. O da size runtime bu interface nizin canlı ve çalışan bir halini sunuyor.

Refit ile;

  • Get,Post,Put,Delete .. her HttpVerb e karşılık gelen metodlar oluşturabilirsiniz
  • Querystring, dynamic url parameter s gibi birçok şekilde get request i atabilirsiniz
  • json ve formurlencoded gibi birçok farklı serialization yöntemi ile post lar yapabilirsiniz
  • multipart upload lar yapabilirsiniz
  • static veya dynamic header ları kolayca request lerinize ekleyebilirsiniz

ve daha nicesi..

Aşağıdaki basit bir refit interface i görüyoruz. Bu interface e baktığımız da şunu anlıyoruz.

Relative url i olmayan bir endpoint e GET request i atılacak string olarak results adında bir querystring parametresi geçilecek. Geriyede string bir result dönecek. Gidecek olan request e de Content-Type : application/json header ı eklenecek.

namespace HttpClientFactoryRefit
{
    [Headers("Content-Type : application-json")]
    public interface IRandomUserApi
    {
        [Get("/")]
        Task<string> GetUser(string results);
    }
}

Peki daha önceden 3 farklı yöntem de HttpClientFactory Kullanımı görmüştük. Hatta Typed Client kullanırken tam olmasada benzer bir şekilde Client sınıfımızı ayrıca yazmıştık. Peki Elimizde varolan bir proje var diyelim, önceden de refit kullanmıştık. Ama artık HttpClientFactoryde kullanmak istiyoruz dersek ne yapacağız.

Sadece projemize nugetten

Refit.HttpClientFactory paketini indirdikten sonra, Startup dosyamızdaki ConfigureServices metodunda aşağıdaki şekilde tanımlamamızı yapabiliriz.

Tek fark önceden olduğu gibi AddHttpClien değil, artık AddRefitClient metodunu kullanıyo olmamız. Bu extension metod, yukarıdaki paketi indirdiğimizde gelecek.

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

                services.AddRefitClient()
                .ConfigureHttpClient(client =>
                {
                    client.BaseAddress = new Uri("https://randomuser.me/api");
                });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

Peki kullanımı nasıl?

Kullanımının TypedClient kullanımından hiçbir farklı yok. Refit interface imizi sanki bir TypedClient mış gibi Controller ımızda tanımlıyoruz, ve constructor de bize inject edilecek olan interface olarak atamamızı yaptıktan sonra, dosya içerisinde istediğimiz yerde interface içerisinden yazdığımız istediğimiz metodu kolayca çağırıyoruz.

 public class HomeController : Controller
    {
        private readonly IRandomUserApi _randomUserApi;
        public HomeController(IRandomUserApi randomUserApi)
        {
            _randomUserApi = randomUserApi;
        }

        public async Task Index()
        {
            var result = await _randomUserApi.GetUser("10");
            return View();
        }

         ....

Bu kadar basit. Zaten refit kullandığınız ama HttpClientFactory e geçmediyseniz kodlarınızda çok az değişiklik yaparak kullanmaya başlayabilirsiniz.

Yeni başlayacağınız projelerde her ikisinide kullanmanızı öneririm.

Bir sonraki yazımda görüşmek üzere.

 

Asp.Net Core da HttpClientFactory Kullanımı (Named and Typed Clients)

Selamlar,

HttpClient Factory nin Asp.Net Core tarafında kullanımı ile ilgili yazılarımın bu bölümünde NamedClient  ve TypedClient kullanımlarına bakacağız.

Önceki yazılarımda HttpClientFactory hakkında teorik olarak getirdiklerinden ve Basic seviye de kullanımından bahsetmiştim.

Şimdi ilk olarak NamedClients dediğimiz şekilde nasıl kullanırız buna bakalım.

Aslında Basic usage dan çok fazla farklı değil ama adı üzerinde bu sefer herbir httpclientfactory e bir isim vermeye başlıyoruz. Özellikle uygulamalarımızda birden çok endpoint ile haberleşeceğimiz düşünün (ki genelde böyle olur). Bunların hepsi ile haberleşirken aynı konfigürasyonu kullanmak yeterli olmayabilir. Dolayısı bu haberleşmeler için kullanacak olduğumuz client ları ayrı ayrı tanımlayıp ayrı ayrı konfigüre etmek daha doğru olacaktır.

Bu ayırt edebilmenin de en kolay yollarından biri her bir client a bir isim vermek 🙂

Aşağıdaki örnekte bir asp.net core startup dosyasında namesclient nasıl tanımlarız bunu görüyoruz. Önceki basic usage ile kıyasladığımız da tek fark sadece string bir isim vermiş olmamız. Bu örnekte isim olarak randomuserclient dedik.

 public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddHttpClient("randomuserclient", client =>
            {
                client.BaseAddress = new Uri("https://randomuser.me/api");
                client.DefaultRequestHeaders.Add("Accept", "application/json");
                client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting");
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

Bundan sonrasında da yine Basic usage a oranla kullanımında da pek fark yok. Yine IHttpClienFactory interface imizi HomeController a otomatik olarak inject olan olacak şekilde atama yapıyoruz. Sonrasında da yine CreateClient metodu ile httpclient objemizi oluşturup işlemlerimizi yapabiliyoruz. Burada sadece tek fark CreateClient derken, Startup dosyasında vermiş olduğumuz isim ile çağırıyor olmamız.

public class HomeController : Controller
    {
        private readonly IHttpClientFactory _httpClientFactory;

        public HomeController(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        public async Task Index()
        {
            var client = _httpClientFactory.CreateClient("randomuserclient");
            var result = await client.GetAsync("?results=5");

            ViewBag.statusCode = result.StatusCode;
            return View();
        }

Gerçek hayat senaryolarında Basic usage ve NamedClient ları kullanıyor olmanız çok olası değil aslında. Ama varlıklarını ve kullanım şekillerini bilmekten zarar gelmezdi. 🙂

Gelelim herbir client ımızı ayrı bir sınıf olarak tanımlayacağımız kullanım şekline. Daha gerçekçi bir senaryo olarak şunu düşünebiliriz. Örneğin projemizde “Customer” entityleri için yazılmış bir API var, ve CRUD işlemleri yapıyor. Bu Api ile haberleşecek yapıyı, Client projesinde bi HttpClientFactory typed client ı olarak tanımlayıp, Gidilecek her bir farklı relative url için (/create, /update, /delete, /get vb.)  ayrı ayrı metodlar olarak tanımlamak ve birarada tutmak hoş olmaz mıydı?

Bence de hoş olurdu:) Aşağıda randomuser api ile haberleşmesini istediğimiz Endpoint için tüm işlemlerimizi yazacağımız bir TypedClien oluşturalım.

Sınıf dikkat ederseniz hiçbir özel sınıftan veya interface den miras almıyor. En altta tanımladığımız bir interface miz var. tek bir metoda sahip, Concrete class ımız olan RandomUserClient buradan implemente oluyor ve ekstra bir metoda daha sahip.

Burada en önemli nokta Client içerisnde readonly olarak tanımladığımız ve Constructor ımıza otomatik olarak inject edilecek olan HttpClient objesi. Bu obje biz bu client ımızı startup dosyasında TypedClient olarak service lere eklerken bizim için inject olucak şekilde hazırlanıyor framework tarafından.

Yani bir typed client yazarken bir properymiz HttpClient tipinde olacak, ve bu TypedClient içerisindeki network haberleşmelerini, constructor ımızla bize inject edilcek olan client ile yapacağız.

namespace HttpClientFactoryTypedClient
{
    public class RandomUserClient : IRandomUserClient
    {
        public RandomUserClient(HttpClient client)
        {
            Client = client;
        }

        public HttpClient Client { get;  }

        public async Task GetStringContentAsync()
        {
            return  await Client.GetStringAsync("?results5");
        }

        public async Task GetStringDataLength()
        {
            var result = await Client.GetStringAsync("?results5");
            return result.Length;
        }
    }

    public interface IRandomUserClient
    {
        Task GetStringDataLength();
    }
}

Bu oluşturmuş olduğumuz typed client ı startup dosyasında aşağıdaki şekilde register edebiliriz.

 public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            //services.AddHttpClient(client =>
            //{
            //    client.BaseAddress = new Uri("https://randomuser.me/api");
            //    client.DefaultRequestHeaders.Add("Accept", "application/json");
            //    client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting");
            //})
            //.SetHandlerLifetime(TimeSpan.FromMinutes(5));

            services.AddHttpClient<IRandomUserClient, RandomUserClient>(client =>
            {
                client.BaseAddress = new Uri("https://randomuser.me/api");
                client.DefaultRequestHeaders.Add("Accept", "application/json");
                client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting");
            })
            .SetHandlerLifetime(TimeSpan.FromMinutes(2));
            


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

IRandomUserClient interface ile e register ediyoruz ki, ileride bu implementasyonu değiştirmek istersek, projede burası dışında başka yerde kod değiştirmek zorunda kalmayalım burada sadece

services.AddHttpClient<IRandomUserClient, NewRandomUserClient>()….

diyerek hayatımıza devam edebilelim.

Peki bu typed client ımızı projede nasıl kullanacağımıza bakarsak, aşağıdaki gibi olacak.

Bu sefer basic ve named client kullanımında olduğu gibi IHttpClienFactory değil doğrudan kendi yazdığımız client ı field olarak oluşturuyoruz ve Constructorda da bize o inject ediliiyor. Aşağıda da yine eskiden olduğu gibi kullanabiliyoruz. Tek farkı, CreateClient metodu yerine Client propertysini kullanıyoruz. Bu da hemen yukarıda kendi TypedClient ımız içerisinde tanımlamış olduğumuz HttpClient tipindeki propertymiz idi.

public class HomeController : Controller
    {
        private readonly IRandomUserClient _randomUserClient;

        public HomeController(IRandomUserClient randomUserClient)
        {
            _randomUserClient = randomUserClient;
        }

        public async Task Index()
        {
            var result = await _randomUserClient.Client.GetAsync("?results=5");
            ViewBag.statusCode = result.StatusCode;

            var stringResult = await _randomUserClient.GetStringContentAsync();

            //var contentLength = await _randomUserClient.GetStringDataLength();

            return View();
        }

 

HttpClientFactory hakkında bayağı bilgi sahibi olmuş olduk. Daha yazacaklarım var bu konuda, örneğin Refit ve Polly ile kullanımı nasıl. asp.net core dışında bu httpclient factory i nasıl kullanırız vs vs..  Şimdilik bu kadar.

Bir sonraki yazımda görüşmek üzere.

Asp.Net Core da HttpClientFactory Kullanımı (Basic Usage)

Selamlar,

Bir önceki yazımda HttpClient kullanım hataları ve HttpClientHandler ile alakalı çıkan sıkıntılardan ve bunlara HttpClientFactory ile gelen düzeltmelerden bahsetmiştim. Bu yazımda HttpClientFactory nin bir asp.net core projesinde farklı kullanım şekillerini göreceğiz.

HttpClientFactory yi bir Asp.Net Core projenizde kullanabilmenizin 4 farklı yöntemi mevcut

Bunlar;

  • Basic Usage
  • Named Clients
  • Typed Clients
  • Generated Clients

Bunlardan ilki ile incelemeye başlayalım.

Haberleşeceğimiz url yine randomuser/me olsun.

Öncelikle Asp.Net Core Projemizdeki Startup dosyasında service lerimiz konfigüre ettiğimiz ConfigureServices metodu içerisine AddHttpClient diyerek, proje içerisinde HttpClientFactory kullanacağımızı söylemiş oluyoruz.

 public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            //Basic Implementation
            services.AddHttpClient();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

Bu işlemden sonra artık HttpClientFactory en basic hali ile kullanımımıza hazır hale geliyor.

Peki bir asp.net core projemiz deki bir controller içerisine gidelim ve orada bu HttpClientFactory yi nasıl kullanacağımıza bakalım.

 public class HomeController : Controller
    {
        private readonly IHttpClientFactory _httpClientFactory;

        public HomeController(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        public async Task Index()
        {
            var client = _httpClientFactory.CreateClient();
            var result = await client.GetAsync("https://randomuser.me/api?results=5");

            ViewBag.statusCode = result.StatusCode;
            return View();
        }

          ....

Yukarı daki kod bloğunda gördüğümüz gibi en temel şekilde Basic Usage kullanımı olarak IHttpClientFactory sınıfını kullanabiliriz.

Asp.Net Core un kendi dependency injection paketi i, ConfigureServices metodunda AddHttpClient  dediğimiz anda, biz uygulamanın herhangi bir yerinde bu sınıfı istiyoruz dediğimizde bize istediğimizi veriyor olacak.

Burada HomeController ın constructor ında IHttpClientFactory bizim için inject edilmiş oluyor. Bizde bunu yukarıdaki fieldımızda saklayıp aşağıdaki  Index action ı içerisinde kullanabiliyoruz.

_httpClientFactory.CreateClient() dediğimiz her noktada aslında arkada yeni bir HttpClient instance ı oluşuyor. Bu, özellikle bir önceki yazımı okudu iseniz garip gibi gelebilir ilk başta ama aslında hatırlarsak sorun HttpClient ta değil HttpClient Handler da idi.

HttpClientFactory her CreateClient dediğimizde yeni bir HttpClient instance ı oluşturuyor fakat arka tarafta HttpClienthandler ları pool mekanizmasına soktuğu ve onların lifetime ını kendi yönettiği için normal httpclient kullanırken karşlılaştığımız sorunlarla karşılaşmıyoruz.

 

Asp.Net Core 2.1 İle Gelen HttpClientFactory ve HttpClient İle Kıyaslama.

Selamlar,

Üzerinden 3 ay kadar geçmiş olsa da, Eylül 2018 de Microsoft Türkiye tarafından düzenlenen etlinlikte bahsettiğim ve uzunca zamandır, hakkında konuşmak istediğim HttpClientFactory ve HttpClient hakkında yazmaya henüz başlayabiliyorum.

Öncelikle HttpClient dan, yanlış kullanımlarından ve doğru kullanım olsa dahi handikapları nelerdi bunlardan bahsedip, sonrasında HttpClientFactory hakkında yazacağım.

Özellikle uygulamalar arasındaki haberleşme konuları, ve bu alanda kullanılabilecek yeni teknolojiler ilgi alanımda olduğu için, (Resilient network services hakkında yazılarımı buradan başlayarak okumanızı öneririm) bu konuda yazmak istediğim çok şey var.

Önce kısa bir özet geçelim. Eğer bir .net developer iseniz ve dış dünya ile haberleşecek mekanizmalar yazmanız gerekiyor ise (http üzerinden), sırası ile .netframework tarafından bize sunulan API lar şu şekilde idi;

  • HttpWebRequest
  • WebClient
  • HttpClient

Özellikle son senelerde uygulamamlarımızda bir haberleşme olacak ise dış dünya ile(http protokolü ile), kolay kullanımı ve birçok isteri yerine getirebilmesinden, async desteğinden vs çokça sebepten ilk seçenek hemen hemen herkes için HttpClient oluyor.

Aşağıda yanlışları ile birlikte kolay kullanımıına bir örnek görüyoruz. Bir for döngüsü içerisinde, tıpkı microsoft un best-practice lerinde söylediği gibi using bloğu içerisinde bir endpoint e request atıyoruz. Burada diyebilirsiniz ki, – Client ı neden for un içerisinde yazdık dışında yazalım –  doğru diyorsunuz derim. Ama gelmek istediğim nokta başka.


for (int i = 0; i < 13; i++)
{
     using (var httpClient = new HttpClient())
      {
            var result = await httpClient.GetAsync("https://randomuser.me/api?results=5");
            Console.WriteLine($"{result.StatusCode} - {result.IsSuccessStatusCode}");
      }
}

Microsoft der ki;

“Eğer bir api IDisposable dan miras alıyor ise, onu using bloğu içerisinde kullanın”

Aşağıdaki resimde görmüş olduğunuz gibi, HttpClient, HttpMessageInvoker dan o da, IDisposable dan türüyen bir sınıf. Bu durumda bu HttpClient objesini using bloğu içerisinde kullanmakta bir sıkıntı yok gibi düşünebiliriz.

Capture

Lakin ki durum öyle değildir 🙂

Netstat.exe komutunu çalıştırarak pc mizden dış dünyaya açılan Socket lerin durumuna bir bakalım.

Bu arada socket dediğimiz de anlayacağımız şudur. İki bilgisayarın birbiri ile haberleşmesi sırasında birbirlerine verdikleri adres. Sadece IP adresi yeterli değildir bunun yanında bir de port numarası önemlidir.

Ör: browser dan google.com u açtınız. Client tarafındaki socket adresi:ClientIP+60432(dynamic port number)

Aradaki bağlantı ise : ClientIP+dynamicPort —- GoogleIP+80(standart port) şeklinde olucaktır. Session bittikten sonra aynı port yeniden kullanılabilir.

Peki dönelim netstat.exe nin sonuçlarına. Aşağıda ki resimde görebileceğiniz gibi. Ben Console uygulamamı durdurmuş olsam bile yine de HttpClient objelerinin dışarıya açmış olduğu socket lerin TIME_WAIT state inde bekliyor olduklarını görüyorum.

Capture.PNG

Yani aslında client tarafı connection ı kapamış ama eksik kalan paketler olabilmesi vs adına halen socket ler açık.Windows için global olarak bu bekleme süresi 240 sn.

Bu değeri değiştirmemiz mümkün;

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay

ile yeni istediğimiz değeri set edebiliriz.Peki bu şekilde bir kullanım sonunda uygulamanın herhangi bir t anında alabileceğimiz muhtemelen hata nedir?

System.Net.Sockets.SocketException

Peki önce bu genel HttpClient kullanım hatasının çözümüne gelelim. Çözüm aslında basit. Singleton bir HttpClient kullanmak.

private static HttpClient client = new HttpClient();

for (int i = 0; i < 13; i++)
{
	var result = await client.GetAsync("https://randomuser.me/api?results=5");
       Console.WriteLine($"{result.StatusCode} - {result.IsSuccessStatusCode}");
}

Peki client ı singleton yaparak farklı farklı httpclient objeleri oluşturmaktan ve dolayısı ile aslında kapanmış olan connectionların açtığı boşuna bekleyen açık socket israfından kurtulmuş oluyoruz. Fakat bu da beraberinde farklı sorunlar getiriyor.

Bu durumda longlived HttpClient lar yüzünden DNS değişikliği veya Azure tarafında otomatize edilmiş Staging-Production ortamları arasındaki geçişlerde halen eski adreslere gitme vs vs gibi farklı sorunlar karşımıza çıkıyor.

Bunlara da çözüm olarak; DefaultRequestHeaders. ConnectionClose true diyerek http keep-alive-header = false göndermek ve ya ServicePointManager api sini kullanarak kuracağımız bağlantı üzerinde daha low level ayarlar yapmak da mümkün. Fakat herkes tarafında net olarak evet çözüm budur denen bir durum yok.

Client.DefaultRequestHeaders.ConnectionClose = true;

var sp = ServicePointManager.FindServicePoint(new Uri(” https://randomuser.me/api?results=5 “)); 

sp.ConnectionLeaseTimeout = 60*1000;

aşağıdaki github issue sunda konuşulan sıkıntıları fikir sahibi olmanız açısında okumanızı öneririm

https://github.com/dotnet/corefx/issues/11224

Peki gelelim HttpClientFactory e. Bu Asp.net core 2.1 ile gelen api bize neler sunuyor bir bakalım.

HttpClientFactory

Bu api aslında şuan .net dünyasında bir haberleşme durumunda kullanmanız gereken en taze iyi api diyebilirim. Yani henüz başlamadı iseniz kullanmaya, bundan sonraki tüm Asp.Net Core projelerinizde kullanmaya başlamanızı şiddetle tavsiye ederim.

4 temel özelliğinden bahsedebiliriz;

  1. Tüm haberleşmenin merkezi bir şekilde tanımlanma ve konfigüre edilmesi imkanını sunuyor bize
  2. Refit, polly gibi .net foundation dünyasında önemli yeri olan kütüphanelerin extensionları yazıldı bile. Poly-based middleware extensionlar ve delegating handler lar ile request ve responselar üzerinde istediğimiz oynamaları yapabiliyoruz. Bir çeşit interceptor gibi düşünebilirsiniz.
  3. Yukarıd da bahsettiğim HttpClient görünümlü sıkıntıların asıl sebebi aslında HttpClientHandler.  HttpClienFactory de bu HttpClientHandler ın lifetime ını pooling mekanizması sayesinde daha doğru yönettiği için yukarıdaki sıkıntılardan da kurtulmuş oluyoruz.
  4. Kendi Factorysi tarafından oluşturulmuş tüm request ve responların loglanması otomatik olarak sağlanıyor.

 

Peki HttpClientFactory bu sorunlara nasıl çözüm getiriyor?

Şöyleki;

  • HttpClientHandler instance ları pool a alınır ve lifetime ları yönetilir. Default 2 dakika süre ile bu pool da yeni oluşturulmuş olan HttpClient instancelarının kullanımına hazır halde beklerler.HttpClient instance ları yaşadığı sürece, HttpClientHandler da yaşar.
  • 2 dakika sonra expired olarak işaretlenirler. Dolayısı ile, CreateClient ile yeni bir HttpClient instance I oluşturulduğunda, onun kullanabileceği durumda olmaz. Hemen dispose olmaz, çünkü başka dispose olmamış HttpClient ların önceden kullanımına alınmış olabilir.
  • HttpClientFactory, arkada bir background service kullanır ve expire olarak işaretlenmiş ClientHandler I takip eder. Artık referans edilmediklerinde de dispose eder ve connectionları kapar.
  • Pooling feature sayesinde, Socket exhaustion riskini azaltır. Refreshleme mekanizması da DNS update I gibi problemlere (long lived httpclienthandler lar yok artık =) ) çözüm getirir.

HttpClientFactory nin 4 farklı kullanım şekli mevcut.

  • Basic Usage
  • Named Clients
  • Typed Clients
  • Generated Clients

 

Teoril olarak HttpClient ve HttpClientFactory hakkında biraz bahsettikten sonra bundan sonraki yazılarımda bu farklı kullanım şekillerini göreceğiz.

Bir sonraki yazımda görüşmek üzere.