Erhan Ballıeker

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.

Resilient Network Services Bölüm 7 – Xamarin Projemizde Refit, Polly, Akavache, Fusillade ve ModernHttpClient Birlikte Kullanımı Bölüm 2

Selamlar,

Bir önceki yazımda önhazırlığını tamamladığımız yazımızın asıl kısmına geçelim.  En son IApiRequest adında bir interface oluşuturarak içerisine 3 adet readonly property koymuştuk. Şimdi hızlı bu interface implemente edecek olan gerçek ApiRequest sınıfımızı oluşturalım ve ilgili kütüphaneleri kullanmaya başlayalım.

ApiRequest adında bir class ekleyelim projemize, içeriği aşağıdaki şekilde olacak.

public class ApiRequest : IApiRequest
    {
        private readonly Lazy _background;
        private readonly Lazy _userInitiated;
        private readonly Lazy _speculative;

        public string BaseApiAddress = "https://randomuser.me/api";

        public ApiRequest()
        {
            _background = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.Background), BaseApiAddress));
            _userInitiated = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.UserInitiated), BaseApiAddress));
            _speculative = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.Speculative), BaseApiAddress));
        }

        public T Background => _background.Value;

        public T Speculative => _speculative.Value;

        public T UserInitiated => _userInitiated.Value;

        public T CreateClient(HttpMessageHandler handler, string baseApiAddress = null)
        {
            var client = new HttpClient(handler)
            {
                BaseAddress = new Uri(baseApiAddress ?? BaseApiAddress)
            };

            var res = RestService.For(client);
            return res;
        }
    }

Peki gelelim yukarıda ki class ımızı satır satır inceleyelim. Bu class ımızda bu seri boyunca bahsettiğimiz kütüphanelerden hali hazırda 3 tanesini kullanmış olduk. Bunlar Fusillade, Refit ve ModernHttpClient. Sınıfımız incelersek şunları görüyoruz.

  • class ımız IApiRequest Interfaceinden türüyor, ve buradan alması gereken propertyler aşağıdaki gibi oluşturuldu.
    • public T Background => _background.Value;
    • public T Speculative => _speculative.Value;
    • public T UserInitiated => _userInitiated.Value;
  • Yukarıda bu propertylerin initalize edilme kısmını C# ın Lazy objesine bıraktık. 3 adet field ımızı class ilk başında tanımlandı. Bunların tipi Lazy olarak işaretlendi. Ve ApiRequest sınıfmızın constructor nda da bu Lazy field ların neyi ne şekilde initiazlize edileceklerini tanımladık;
    • 3 Lazy adet field ımız
      • private readonly Lazy _background;
      • private readonly Lazy _userInitiated;
      • private readonly Lazy _speculative;
    • Constructor da bunların yine Lazy sınıfının constructor ını kullanarak ne şekilde ne döneceklerini tanımladık. Aşağıda yazmış olduğumuz CreateClient metodu ile geriye sınıfı oluştururken verdiğimiz generic parametresini geri dönüyoruz.  Constructor da kullandığımız bu RateLimitiedHttpMessageHandler ve NativeMessageHandler a dikkat edelim.YAzmış olduğumuz CreateClient metodu aslında bizim için bir HttpClien oluşturacak bunu biliyoruz. Parametre olarak bu method bizden bir HttpMessageHandler istiyor.  Bu metoda Fusillade in RateLimitedHttpMessageHandler ını veriyoruz ve priority oralark da her bir interface imizden gelen her bir property karşılık olan Fusillade Priortiy lerini veriyoruz.  RateLimitedHttpMessageHandler  içerisine contructor parametresi olarak bir de ModernHttpClient ın  NativeMessageHandler ını veriyoruz. Bu sayede en alttaki CreateClient metodumuza geçmiş olduğumuz HttpMessageHandler, ModerHttpclient ın handler ını kullanan Fusillade in RateLimited handler ı olmuş oldu. Bu ikisini bir arada bu şekilde kullanabiliriz.
      • _background = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.Background), BaseApiAddress));
      • _userInitiated = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.UserInitiated), BaseApiAddress));
      • _speculative = new Lazy(() => CreateClient(new RateLimitedHttpMessageHandler(new NativeMessageHandler(), Priority.Speculative), BaseApiAddress));
    • Interface den gelen Propertylerimiz class ımız üzerinden seçildiğinde, LAzy sınıfı bizim ona verdiğimiz delegate i kullanarak bu sınıfı oluştururken verdiğimiz generic T objesini bizim için oluşturacak.
  • CreatClient metoduna bakalım.
    • var client = new HttpClient(handler)
    • { BaseAddress = new Uri(baseApiAddress ?? BaseApiAddress) };
      • Burada yeni HttpClient objesi oluşturup bunun runtime da bizim istediğimiz yaşayan Refit interface ini alıp haberleşme yapısı olarak geri vermesi için Refit in RestService.For generic metodu nu kullanıp içeriye client ımızı veriyoruz.
    • var res = RestService.For(client); return res;

Peki hemen bir de madem Refit i de kullandık, ilk yazıda belirttiğim bu endpoint ile haberleşecek ve userları bize getirecek olan refit Interface imizi oluşturalım.

Ben genelde bu refit interfacelerini proje içerisinde Endpoints diye bir klasör altında toplarım. Burada da öyle yapalım Endpoints diye bir klasör açalım ve içerisine

IRandomUserEndpoint diye bir interface ekleyelim

    [Headers("Content-Type : application/json")]
    public interface IRandomUserEndpoint
    {
        [Get("/")]
        Task GetUserList(int results, int page);
    }

Interfaceimizin içeriği yukarıdaki gibi olacak. Refit ile ilgili postlarımda refit kullanarak e şekilde requestleri nasıl metod haline dönüştürdüğümüzün detaylarına girmiştim. Burada özetle;

  • her bir request e “Content-Type : application/json” header ı ekliyoruz.
  • Get metodu ile direk olarak verilen base url gittiğimizi Get metodunu kullanarak söylüyoruz.
  • results ve page parametreleri de url de karşılıkları olmadığı için otomatik olarak querystring olarak request url ine eklenecekler.

Bu şekilde bahsettiğmiz url ile haberleşecek interface ide oluşturmuş olduk. gerisi refit e kalıyor.

Tüm bu yazdıklarımızdan sonra bir özetlemek istersek ne şekilde bunları kullanabiliriz ve neler olmuş olur şöyle bir üzerinden geçelim.

Uygulamada şu kodu yazdığımız anda;

IApiRequest<IRandomUserEndpoint>.UserInitiated

dediğimiz de Refit bizim için yazdığımız interface in yaşayan bir httpclient halini üretecek. Bu HttpClient a verdiğimiz Fusillade nin RateLimitedHttpMessageHandler ve ModernHttpClient ın NativeMessageHandler  ları refit oluşturacağı HttpClient ın HttpMessageHandler ları olarak kullanılacak. Yani Refit e oluşturmasını istediğimiz HttpClient nasıl bir haberleşme yağacağını söylüyoruz, bu esnada hangi handlerları kullanmasını istediğimizi söylüyoruz ve sonrasında ortaya Refit, Fusillade ve ModernHttpClient ın bir arada kullanılmış olduğu güzel bir client objesi elimze geçiyor. Ve bunun üzerinden kullanımımıza devam ediyoruz.

Bu post u burada bırakıyorum. Bir sonraki post umda bu serinin son yazısını yani kullanıma Polly e Akavache yi de nasıl dahil edeceğimize bakarak, tamamlamış olmayı umut ediyorum 🙂

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

Resilient Network Services – Bölüm 4 – ModernHttpClient

Selamlar,

Bu yazımda sizelere ModernHttpClient kütüphanesinden ve uygulamanıza performans olarak katkılarından bahsedeceğim.

ModernHttpClient kullanması belki de en basit kütüphane olabilir 🙂 ama etkileri fazlaca.

Bir xamarin uygulamanızda HttpClient ile beraber yapacağınız requestleri u kütüphane ile yaptığınızda çok hızlı çalıştığını göreceksiniz. Bunun sebebi ise bu kütüphanenin arkada platform spesifik taraflarda kullandığı native kütüphaneler. Bu kütüphaneler şunlar

Native development yapmış olanlarınız ya bu kütğphaneleri doğrudan kullanmıştır ya da bu kütüphaneleri arkaplanda kullanan başka kütüphaneler kullanmıştır.(AFNetwork gibi)

Bu iki kütüphane hakkında hiçbir fikriniz olmasa bile uygulamanızda bu ikisini kullanıp daha hızlı bir API haberleşmesi için yapmanız gereken sadece şu;

var httpClient = new HttpClient(new NativeMessageHandler());

Xamarin projenizde sadece Portable yada standard projenize ModernHttpClient ı referans ederek bu şekilde kullanıma başlayabilirsiniz.

Kütüphanenin kullanımı kadar kısa ama kendisi kadar faydalı bir yazı olduğunu umarım 🙂

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

Resilient Network Services Bölüm 3 – Fusillade

Selamlar,

Önceki yazılarımda Resilient Network services kurgusundan ve bu kurguyu yaparken bize çok destek olacak olan kütüphanelerden bahsetmiştim.  En son REFIT ten bahsetmiştik, şimdi sıra geldi Fusillade den bahsetmeye.

Bu da yine mobil ve desktop uygulamalırınızda daha efektif işler yapabilmenizi sağlayan, Volley ve Picasso kütüphanelerinden esinlenerek oluşturulmuş bir C# kütüphanesi. Volley başarılı bir Http haberleşme kütüphanesi android tarafında. Picasso ise Android developer ların olmaz ise olmalardından bir tanesi. Peki bakalım bu Fusillade de bunlardan esinlenerek neler  yapılmış.

Öncelikle hangi ortamlarda bu kütüphaneyi kullanabileceğimizi görelim.

  • Xamarin.Android
  • Xamarin.iOS
  • Xamarin.Mac
  • Windows Desktop apps
  • WinRT / Windows Phone 8.1 apps
  • Windows Phone 8

Bu ortamların hepsinde bu kütüphaneyi kullanmamız mümkün. Kendisi portable bir kütüphane, NetStandard kütüphanesi kullanmaya başlayanlarda rahatlıkla kullanabilirler.

Peki bu kütüphane bize neler sağlar bunlara bir bakalım. Fusillade temelde HttpMessageHandler kümesi diyebiliriz. HttpClient ile yaptığımız requestlerde arka tarafta bu kullanıldığından, Fusillade kullanarak yaptığınız reuestlerin daha iyi test edilmiş senaryolar üzerine kurulu HttpMessageHandler ile çalışmasından dolayı daha verimli ve responsive uygulamalar yazmanızda yardımcı olur. Özetle Fusillade bize 4 temel fayda da bulunuyor.

  • Auto-deduplication of relevant request: Türkçe mealine gelecek olursak bu deduplication özelliğinin bize sağladığı şey şu; örneğin bir mobil uygulamanız var, sosyal medya olsun. Kullanıcıların postlarını aşağıya doğru diziyorsunuz, bir arkadaşınız 3-5 post atmış. Bunları gösterirken yanında profil resmini de göstereceksiniz değil mi? E bu resimde çok büyük olasılıkla uygulamaya gömülü olmayacağına göre network üzerinden istek atıp alacağınız bir resim olucaktır. İşte bu ve benzeri durumlarda, Fusillade birbirine benzer requestler için yalnızca tek bir request atıyor ve dönen result ı tüm instance lar ile paylaşıyor. Bu da bize daha az network trafiği oluşturucağımız için performans artısı olarak geri dönüyor.
  • Request Limiting : Bu özellik bize Fusillade tarafından anlık yapılan istekleri kısıtlama imkanı sağlıyor. Instagram’ı düşünelim, like butonuna sürekli basıp duruyoruz, her seferinde request atmak yerine bu gibi davranışları kısıtlamak istediğimiz durumlarda bu özelliğini kullanabiliriz. Yapılan istekler, Volley default u olarak anlık 4 tane olacak şekilde limitleniyor. Bunun la istediğiniz gibi oynayabilirsiniz tabii ki.
  • Request Prioritization: Bu özellik aslında en çok kullandığım Fusillade özelliklerinden biri. Uygulamanızda bir ekranda bir çok widget olduğunu aynı zamanda bunlar load olurken de uygulama responsive olduğu için kullanıcının aynı zamanda birşeylere basıp kendi birşeyleri tetiklediğini düşünelim. İşte bu gibi durumlarda Fusillade hangi requestleri önceliklendirip hangilerini geri planda bırakabileceğini söyleyebiliyorsunuz.
  • Speculative requests: Bu da yine özellikle mobil uygulamalrda kullanabileceğiniz güzel bir özellik. Örneğin ilan listesi açtınız uygulamanızda, en üstte de 5 adet reklam var, tıklanma olasılığı yüksek ilanlar. İşte Fusillade ye şunu söyleyebiliyorsunuz; 5mb data sınırın var, bu 5 adet reklamın requestlerini speculative olarak yolla, 5mb bulmadığın sürece bunların detaylarını al getir, 3. de 5mb doldu mu, o zaman gerisini getirme. Bu kullanıcıların tıklama olasılıklarını yüksek gördüğünüz senaryolarınızda requestleri önceden belli bir limit verip yollamınıza ve kullanıcı tıkladığında hiç bekletmeden doğruca ilgili cevabı göstermenize yarıyan bir özellik.

Kullanım Şekli

Fusillade yi kullanmaya başlamanın en kolay yolu NetCache  adındaki sınıf. Yukarıda yazdığım birçok özelliği zaten kapsıyor.

public static class NetCache
{
    public static HttpMessageHandler Speculative { get; set; }
 
    public static HttpMessageHandler Background { get; set; }
    public static HttpMessageHandler UserInitiated { get; set; }
}

Bu sınıfı aşağıdaki şekilde HttpClient ile beraber kullanabilirsiniz.Aşağıda ilgili request e atılacak olan isteğin kullanıcı tarafından tetiklendiğini belirtmiş olduk.

var client = new HttpClient(NetCache.UserInitiated);
var response = await client.GetAsync("https://randomuser.me/api/?results=5");
var str = await response.Content.ReadAsStringAsync();

Console.WriteLine(str);

Speculative requestlerin limiti şu şekilde değiştirebiliriz.

NetCache.Speculative.ResetLimit(1048576 * 5/*MB*/);

Offline Desteği

Cache için ben genelde Akavache kullanıyor olsamda, yada primitive tipler için platform spesific taraflardaki SharedPreference ve NSUSerDefaults u kullanıyor olsam da, Fusillade bize responseları cache leme ve offline olarak uygulamayı çalıştırma fırsatı da veriyor. Cache için olan objenizi NetCahce in RequestCache Propertysine set ettiğiniz anda gelen responselar burada cachlenir.

NetCache.RequestCache = new MyCache();

var client = new HttpClient(NetCache.UserInitiated);
await client.GetStringAsync("https://httpbin.org/get");

Daha sonra kullanıcının offline olduğu durumda NetCache ile HttpClient a request in  Offline çalışmasını istediğinizi aşağıdaki gibi söyleyebilirsiniz.

var client = new HttpClient(NetCache.Offline);
await client.GetStringAsync("https://httpbin.org/get");

Tüm kütüphane yi github  repo sundan incelemnizi öneririm.

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