Resilient Network Services Bölüm 6 – Polly – 4 (Policy Execution, Configuring, Bulkhead, Cache, Policy Wrap)
Selamlar,
Bir önceki yazımda polly nin Circuit breaker ve fallback özelliklerini incelemiştik. .NetFoundation ailesinden olan bu güçlü kütüphanenin birkaç özelliğinden daha bahsedip örnekler gösterip tüm bu seri dahilinde bahsetmiş olduğumuz 5 kütüphanenin tek bir proje birlikte nasıl kullanabileceğine dair örneklere bakacağız.
Policy Execution
Birçok policy oluşturma şekli gördük Polly ile, bazılarının sonunda Execute metodunu kullanıp neler yapılmasını istediğimizi Polly nin Fluent api yapısı ile nasıl kullandığımızı da gördük. Bu execution işine biraz daha detaylı bakmamız gerekirse, şunlardan bahsedebiliriz.
// Action delegate imizi Policy Execute etoduna doğrudan verebiliriz. var policy = Policy .Handle<SomeExceptionType>() .Retry(); policy.Execute(() => DoSomething()); // execute ederken esktra context parametrelerini, aşağıdaki örnekte // bir Dictionary<string, object> parametresi geçmek gibi, // Policy patternleri içerisinde yakalayıp ekstra işlemler için kullanabiliriz. var policy = Policy .Handle<SomeExceptionType>() .Retry(3, (exception, retryCount, context) => { // Execute içerisine verdiğimiz, dictionary burada context adıya elimizde oluyor. var methodThatRaisedException = context["methodName"]; Log(exception, methodThatRaisedException); }); policy.Execute( () => DoSomething(), new Dictionary<string, object>() {{ "methodName", "some method" }} ); // Geriye bir değer dönen şekilde Execute işlemi yapıp bunu bir variable da // saklayabiliriz var policy = Policy .Handle<SomeExceptionType>() .Retry(); var result = policy.Execute(() => DoSomething()); // Hem geriye değer dönüp hem de kullanacağımız pattern için dışarıdan context // parametreleri verebiliriz. var policy = Policy .Handle<SomeExceptionType>() .Retry(3, (exception, retryCount, context) => { object methodThatRaisedException = context["methodName"]; Log(exception, methodThatRaisedException) }); var result = policy.Execute( () => DoSomething(), new Dictionary<string, object>() {{ "methodName", "some method" }} ); // Daha önce de gördüğümüz gibi, Polly nin FluentApi ile birçok şeyi birarada // yapabiliriz. Aşağıda ki örnekte; // * Hem Sql Exception veya Argument Exception yakalama (Fault-Handling) // * Retry pattern kullanımı // * en sonra execute yapma şeklini görmüş oluyoruz. Policy .Handle<SqlException>(ex => ex.Number == 1205) .Or<ArgumentException>(ex => ex.ParamName == "example") .Retry() .Execute(() => DoSomething());
Timeout
Optimistic Timeout
Optimistic time-out CancellationToken mekanizması ile çalışıyor. Bu yüzden hem çalışacak olan delegate in buna uygun olması hem de execute işleminin async versiyonunun kullanılması lazım.
// 30 sn içerisinde verilen delegate tamamlanmazsa caller a geri döner.
Policy
.Timeout(30)
// time out u timespan olarak konfigüre etmek istersek.
Policy
.Timeout(TimeSpan.FromMilliseconds(2500))
// timeout bir Func<TimeSpan> olarak tanımlamak istersek.
Policy
.Timeout(() => myTimeoutProvider)) // Func<TimeSpan> myTimeoutProvider
// Timeout gerçekleşirse bu timeout işleminden sonra
//ekstra bir delegate kullanmak istersek
Policy
.Timeout(30, onTimeout: (context, timespan, task) =>
{
// ...
});
// örnek olarak hangi policy nin timeout a düştüğünü loglamak istersek.
Policy
.Timeout(30, onTimeout: (context, timespan, task) =>
{
logger.Warn($"{context.PolicyKey} at {context.ExecutionKey}: execution timed out after {timespan.TotalSeconds} seconds.");
});
// Time out işleminden sonra herhangi bir hata varsa bunu yakalayıp loglamak istersek.
Policy
.Timeout(30, onTimeout: (context, timespan, task) =>
{
task.ContinueWith(t => {
if (t.IsFaulted) logger.Error($"{context.PolicyKey} at {context.ExecutionKey}: execution timed out after {timespan.TotalSeconds} seconds, with: {t.Exception}.");
});
});
örnek Execution:
aşağıdaki örnekte, Async olarak çalıştırdığımız policy mizin timeout süresi 30 sn. dikkat ederseniz ExecuteAsync içerisinde yapılan işlem bir CancellationToken alan Http get Request i. ve timeout policy e geçilen CancellationToken ise None. yani ekstra manuel olarak bir cancellation senaryosu istemidiğimiz durum için kullanabiliriz.
Policy timeoutPolicy = Policy.TimeoutAsync(30);
HttpResponseMessage httpResponse = await timeoutPolicy
.ExecuteAsync(
async ct => await httpClient.GetAsync(endpoint, ct),
CancellationToken.None
);
yada;
aşağıdaki şekilde dışarıdan cancellation ı destekleyecek şekilde b ir mekanizma kurabiliriz. Bu şekilde ya timeout olunca yada dışarıdan cancel tetiklendiğinde bu policy caller tarafa geri dönecektir.
CancellationTokenSource userCancellationSource = new CancellationTokenSource();
Policy timeoutPolicy = Policy.TimeoutAsync(30, TimeoutStrategy.Optimistic);
HttpResponseMessage httpResponse = await timeoutPolicy
.ExecuteAsync(
async ct => await httpClient.GetAsync(requestEndpoint, ct),
userCancellationSource.Token
);
Pessimistic Timeout
Verilen delegate in bir time out kontrolü olmaması ve dışarıdan bir cancellation token kullanılarak manuel bir tetiklenmenin olmayacağı durumlarda bu yaklaşımı kullanabiliriz.
Policy
.Timeout(30, TimeoutStrategy.Pessimistic)
Örnek execution:
Policy timeoutPolicy = Policy.TimeoutAsync(30, TimeoutStrategy.Pessimistic);
var response = await timeoutPolicy
.ExecuteAsync(
async () => await FooNotHonoringCancellationAsync()
);
Time out gerçekleştiğinde time out policy TimeoutRejectedException
exception fırlatır.
Bulkhead
// Execution ları maksimum 12 tane eş zamanlı çalışacak şekilde kısıtlamak için.
Policy
.Bulkhead(12)
// Execution ları maksimum 12 tane eş zamanlı çalışacak şekilde kısıtlamak için.
// ve bulkhead içerisinde ki tüm slotlar dolu ise 2 tane action ın yer beklemesi için ayarlamak
Policy
.Bulkhead(12, 2)
// Eğer execution reject olursa çalışmasını istediğimiz delegate i vermek istersek.
Policy
.Bulkhead(12, context =>
{
// ...
});
// Bulkhead in durumunu monitor etmek istersek
var bulkhead = Policy.Bulkhead(12, 2);
// ...
int freeExecutionSlots = bulkhead.BulkheadAvailableCount;
int freeQueueSlots = bulkhead.QueueAvailableCount;
Bulkhead policy ile luturmuş oduğumuz policy ler hata aldığında BulkheadRejectedException
fırlatır.
Cache
cache-aside pattern in uygulandığı öncelikli olarak kendi cache datasından sonu döndürmeye çalışan bulamadığı durumlarda ilgili delegate i çalıştırmak istersek bu yöntemi kullanabiliriz.
var memoryCache = new MemoryCache(new MemoryCacheOptions());
var memoryCacheProvider = new MemoryCacheProvider(memoryCache);
var cachePolicy = Policy.Cache(memoryCacheProvider, TimeSpan.FromMinutes(5));
var cachePolicy = Policy.Cache(memoryCacheProvider, new AbsoluteTtl(DateTimeOffset.Now.Date.AddDays(1));
var cachePolicy = Policy.Cache(memoryCacheProvider, new SlidingTtl(TimeSpan.FromMinutes(5));
var cachePolicy = Policy.Cache(myCacheProvider, TimeSpan.FromMinutes(5),
(context, key, ex) => {
logger.Error($"Cache provider, for key {key}, threw exception: {ex}."); // (for example)
}
);
TResult result = cachePolicy.Execute(context => getFoo(), new Context("FooKey"));
PolicyWrap
Önceden de söylediğimiz gibi birçok Policy i önceden uygulamamızın ihtiyaç duyacağı senaryolara olarak oluşturup
var policyWrap = Policy
.Wrap(fallback, cache, retry, breaker, timeout, bulkhead);
policyWrap.Execute(...)
PolicyWrap commonResilience = Policy.Wrap(retry, breaker, timeout);
Avatar avatar = Policy
.Handle<Whatever>()
.Fallback<Avatar>(Avatar.Blank)
.Wrap(commonResilience)
.Execute(() => { /* get avatar */ });
Reputation reps = Policy
.Handle<Whatever>()
.Fallback<Reputation>(Reputation.NotAvailable)
.Wrap(commonResilience)
.Execute(() => { /* get reputation */ });
Bu kadar Polly detayından sonra artık, şimdiye kadar incelediğimiz bu kütüphanelerin bir arada nasıl bir xamarin projemizde kullanabiliriz buna bakacağız.
Bir sonraki yazımda görüşmek üzere.
Resilient Network Services Bölüm 6 – Polly – 3 (Circuit Breaker, Advanced Circuit Breaker, Fallback Policies)
Selamlar,
Bu seri kapsamında en güçlü kütüphane olduğunu düşündüğüm Polly nin detayalrına devam ediyor olacağım. Önceki yazımda Retry,ForeverRetry, Wait and Retry gibi kullanışlı senaryoları detaylı bir şekilde kullanımını kapsayan örneklerine değinmiştim. Bu yazımda Fallback ler ve Circuit Breaker senaryolarını Polly ile nasıl uygulayabileceğimize değinmek istiyorum.
Circuit Breaker
Belli bir tipte hataların sıklaşması kendini yinelemesi gibi durumlarda, sistemin sürkli hataları responselar almaya başladığı durumlarda, daha fazla zaman kayına uğratmadan uygulamamızı kısıtlamalar yapabilmemiz mümkün. Adından da az çok anlaşılacağı üzere bu pattern zaten, bir hatalı request-response lar loop una girilmiş senaryoda müdahil olup, fail-fast-move-on(” yani çok takılma olut bunlar sen işine bak 🙂 “) yaklaşımı ile duruma el atmamızı sağlıyor.
// Belli bir exception tipinden hatayı söylediğimiz sayıdan fazlaca
// aldığımız durumlarda bu loop u kesmek için
// belli bir bekleme süresi verip circuit-break ettiğimiz yani bu loop u
// kestiğimiz durum
Policy
.Handle<SomeExceptionType>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1));
// Belli tipten bir hatayı belli bir sayıda aldığımız da
// circuit breake i uygulayacağımız süreyi söyleyip, circuit in state inin
// değiştiği durumlarda yapmak istediğimiz aksiyonları söylediğimiz durum
Action<Exception, TimeSpan> onBreak = (exception, timespan) => { ... };
Action onReset = () => { ... };
CircuitBreakerPolicy breaker = Policy
.Handle<SomeExceptionType>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);
// Belli tipten bir hatayı belli bir sayıda aldığımız da
// circuit breake i uygulayacağımız süreyi söyleyip, circuit in state inin
// değiştiği durumlarda yapmak istediğimiz aksiyonları
// ilgili Context i de belirtip kullanarak söylediğimiz durum
Action<Exception, TimeSpan, Context> onBreak = (exception, timespan, context) => { ... };
Action<Context> onReset = context => { ... };
CircuitBreakerPolicy breaker = Policy
.Handle<SomeExceptionType>()
.CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);
// Circuit state ini incelemek istediğimiz durumlarda CircuitState porpertysi
// ile buna ulaşabiliriz. Mesela HEalthMobitoring için vb gibi.
CircuitState state = breaker.CircuitState;
/*
CircuitState.Closed - Normal işlem. Verdiğimiz action lar bu state de iken çalışabilir.
CircuitState.Open - Verdiğimiz bekleme süresinin dolduğu ve circuit in yeniden açıldığı durum. Verdiğimiz action lar bu state de çalışamaz.
CircuitState.HalfOpen - Bekleme süresi dolduğunda yeniden open state geçmeden önceki süreç, bu sırada actionlar çalışabilir ve
state in birsonraki durumda da open mı yoksa closed mu olacaklarına karar verebilirler.
CircuitState.Isolated - Manuel olarak Circuit ın state ini açık tuttuğumuz durum. Action lar bu state de çalışamaz.
*/
// Manuel olarak İlgili circuit ın state ini open tutup çalıştırabiliriz.
breaker.Isolate();
// brekaer ı reset edip closed state e manuel olarak alabiliriz. Bu durumda verdiğimiz
actionların çalışmasının tetiklenmesini sağlamış oluruz
breaker.Reset();
Circuit Open State de iken;
- Verdiğimiz herhangi bir Action delegate çalışmaz
- BrokenCircuitException tipi ile call direk fail olur ve bu fail içerisinde fail olmasının sebebi olan son hata gösterilir (Inner exception olarak)
Aşağıda ki şekilde Circuit Breaker nasıl çalıştığını ve state ler arasındaki geçişin nasıl olduğuna bakalım.
Circuit hayatına ilk olarak Closed state ile başlar. Circuit close olduğunda,
- circuit-breaker verdiğimiz action delegate leri çalıştırır ve bu actionların başarılı sonuçlanmasını ve hata almalarını gözlemler.
- Eğer alınan hataların sayısı verdiğimiz değere ulaşırsa, circuit-breaker state OPEN duruma geçer, tüm exceptionları throw eder.
- Open state de iken action delegate ler çalışmazlar. Yukarıda söylediğimiz gibi circuit-breaker BrokenCircuitException ile hata fırlatır ve break olmasının sebebi olan son hata da InnerException olarak verilir.
- Verilen süre boyunca circuit-breaker OPEN state de bekler. Sonrasında HALF_OPEN state e geçer.
- Bu durumda iken verilen sıradaki action delegate çalışır. Eğer bu action herhangi bir handle edilmiş exception yakalar ise circuit-breaker ın state direk olarak OPEN a geri döner. Eğer hata almadan çalışırsa state CLOSE a döner.
Buradaki ters çalışma mantığı kafa karıştırmasın. Bir elektrik devresi gibi düşünün. Circuit CLOSED state de iken herşey yolunda işler çalışıyor demektir. OPEN state e geçtiğinde ise iş akışı durmuş hata fırlatılmış ve circuit bekletiliyor demektir.
Advanced Circuit Breaker
Polly nin klasik Circuit Breaker ının yetmediği durumlarda AdvancedCircuitBreaker adında daha detaylıca circuit oluşturmanızı sağlayan bir metodu var. Bu metod sayesinde belli bir hata sayısı yerine çalışan action delegate ler içindeki hata oranını, belli bir süre içinde çalışması gereken minimum action sayısını vs vererek, daha dinamik parametrelerle daha ince hazırlanmış bir circuit oluturabilirsiniz.
Policy
.Handle<SomeExceptionType>()
.AdvancedCircuitBreaker(
failureThreshold: 0.5,
samplingDuration: TimeSpan.FromSeconds(10),
minimumThroughput: 8,
durationOfBreak: TimeSpan.FromSeconds(30)
);
Circuit breker ın kullanıldığı yerleri ve kullanım şekilleri detayları için aşağıdaki yazıları da okumanızı öneririm.
- Making the Netflix API More Resilient
- Circuit Breaker (Martin Fowler)
- Circuit Breaker Pattern (Microsoft)
- Original Circuit Breaking Link
Fallback
Bir hata yakalanması işlemi durumunda hata yerine default bir değer dönmek için kullanacağınız yöntem. Örneğin bir ilan resmi yüklemek adına bir request atıldı, hata alındığında bunun yerine default bir imaj basmak senaryosu gibi.
// Gerçek değer alınamadığında default bir değer dönmek için
Policy
.Handle<Exception>()
.Fallback<UserAvatar>(UserAvatar.Blank)
// Default değeri dönerken bir func çalıştırmak istediğimiz de kullanacağımız yöntem
Policy
.Handle<Exception>()
.Fallback<UserAvatar>(() => UserAvatar.GetRandomAvatar()) // where: public UserAvatar GetRandomAvatar() { ... }
// hem bir değer dönmek hemde hata olduğuna dair bir action çalışıtırmak istediğimiz zaman kullanacağımız yöntem.
Policy
.Handle<Exception>()
.Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (exception, context) =>
{
// do something
});
Polly nin Circuit Breaker patterni nin uygulanması ile ilgili bize neler sağladığını ve Fallback senaryolarında yapabileceklerimizi özetle görmüş olduk.
Bir sonraki yazımda son olarak Polly nin Bulkhead, Cache, COnfiguration ve Policy Execution ile ilgili kısımlarına değinip artık tüm bu kütüphaneleri bir xamarin projesinde nasıl kullanırız buna bakmak istiyorum.
Bir sonraki yazımda görüşmek üzere.
Resilient Network Services Bölüm 6 – Polly – 2 (Fault-Response-Handling, Wait, Wait And Retry, Forever Retry Policies)
Selamlar,
Bir önceki yazımda Resilient network services sistemi kurgularken ihtiyacımız olacak belki de en önemli kütüphane olan Polly e teorik olarak bir giriş yapmıştık. Okmadıysanız önce buradan başlamnızı öneririm. Bu yazımızda Polly nin başlıktaki konularını detaylı inceleyip kullanımlarına bakacağız.
Öncelikle Polly tarafında Fault handling kısmına bir bakalım.
Polly Policy si tarafından yakalanmasını istediğiniz hataları tanımlamak
// tek bir hata tipi. Bir HttpREquest exception olduğundan policy nin yakalamasını istiyoruz
//bu hatayı.
Policy
.Handle<HttpRequestException>()
// Spesifik bir hata numarasına göre tek bir hata tipi.
// SqlException lardan sadece 1205 spesific koduna sahip yani sadece Deadlock hatalarını
// yakalamak istediğimizi söylüyoruz.
Policy
.Handle<SqlException>(ex => ex.Number == 1205)
// Birden çok yakalamak istediğimiz exception tipi olduğunda kullanacağımız yöntem
Policy
.Handle<HttpRequestException>()
.Or<OperationCanceledException>()
// Yine birden çok hata tipi için ama her bir hatanın da belli spesifik
// kodlu olanlarını yakalamak istediğimiz zaman kullanacağımız yöntem.
Policy
.Handle<SqlException>(ex => ex.Number == 1205)
.Or<ArgumentException>(ex => ex.ParamName == "example")
// Sıradan hataların yada daha çok async-await kullanırken alacağımız Aggregate Exception lar
//hem top level hemde inner exceptionlarını yakalayabileceğimiz durum. Istersek OperationCanceledException
// da dediğimiz gibi, belli bir condition verebiliriz.
Policy
.HandleInner<HttpRequestException>()
.OrInner<OperationCanceledException>(ex => ex.CancellationToken != myToken)
Request in sonucunda yakalamak istediğimiz Result tiplerine göre Policy ler ayarlamak istediğimiz yöntem
// bir Condition belirterek result ı handle etmek istediğimiz durum.
Policy
.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)
// birden çok result tipini handle etmek istediğimiz durum.
Policy
.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError)
.OrResult(r => r.StatusCode == HttpStatusCode.BadGateway)
// Sadece belli bir response StatusCode lara göre result ı handle etmek istediğimiz durum
Policy
.HandleResult<HttpStatusCode>(HttpStatusCode.InternalServerError)
.OrResult(HttpStatusCode.BadGateway)
// Hem hataları hem de responselaru birlikte yakalamak ve handle etmek
//istediğimiz durum. İstediğimiz status codeları bir array de toplayıp,
//hem exceptionları, hem de bu response statüsüne sahip responları handle edip,
// otomatik bir şekilde Retry mekanizmasını çalıştırdığımız durum.
HttpStatusCode[] httpStatusCodesWorthRetrying = {
HttpStatusCode.RequestTimeout, // 408
HttpStatusCode.InternalServerError, // 500
HttpStatusCode.BadGateway, // 502
HttpStatusCode.ServiceUnavailable, // 503
HttpStatusCode.GatewayTimeout // 504
};
HttpResponseMessage result = await Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))
.RetryAsync(...)
.ExecuteAsync( /* some Func<Task<HttpResponseMessage>> */ )
Özellikle son örneğe dikkat etmenizi istiyorum. Bu örnek ile Polly nin gücünü ve bize sağladığı faydaları artık anlamaya başlayabiliriz. Uygulamamızın atacağı bi request için,
- hata aldığında neler olacağını
- bazı belirlediğimiz status code la dönen responselar için neler olacağını
- bu gibi durumlar gerçekleştiğinde tekrar request göndermek istediğimizi
- ve tüm bu işlemlerden biri olduğunda sonuç olarak ne yapmak istediğimizi yazdığımız func ile, ne kadar fazla senaryoyu 5 satır kod ile Polly e söyleyebiliyoruz.
Api ın kullanım şekli Fluent dediğimiz, metodların nokta ile birbiri ardına yazılması şekline kullanılıyor. Bu da yazım esnasında ayrı bir kolaylık sağlıyor.
Policy nin handle ettiği durumlarda neler yapmasını istediğimiz söylemek istediğimiz durum
Retry
// Yalnızca bir kere tekrar dene.
Policy
.Handle<SomeExceptionType>()
.Retry()
// Birden çok kez tekrar etmesini istediğimiz durum
Policy
.Handle<SomeExceptionType>()
.Retry(3)
// Birden çok tekrar istiyoruz, ve her bir tekrar sonrasında farklı
// bir işlem yapmak istediğimiz durum. Örneğin önyüzde basitçe Tekrar Deneniyor(1.., 2...)
// gibi basit bir notification için.
Policy
.Handle<SomeExceptionType>()
.Retry(3, onRetry: (exception, retryCount) =>
{
// ...
});
// yukarıdaki kullanıma artık olarak, her bir yeni deneme sonrası çalışmasını
// istediğimiz metodu yazarken bize Exception ve retryCount un yanında bir de
// Execute içerisinde kullanmış olduğumuz context i dönen metodun kullanım şekli.
Policy
.Handle<SomeExceptionType>()
.Retry(3, onRetry: (exception, retryCount, context) =>
{
// ...
});
Retry Forever (until succeeds)
Request ten başarılı sonuç alana kadar retry etmek istediğimiz durum. Sınırlı senaryo olsa da böyle durum ile karşılaştığımızda background da çalışan bir request için başarlı sonuç alıncaya kadar retry edebiliriz.
// Sürekli tekrar et.
Policy
.Handle<SomeExceptionType>()
.RetryForever()
// her bir tekrar sonrası alınan hata detayı ile birlikte ne aksiyon
//almak istediğimizi söyleyebildiğimiz yöntem.
Policy
.Handle<SomeExceptionType>()
.RetryForever(onRetry: exception =>
{
// ...
});
// her bir hata sonrası ne aksiyonu almak istediğimizi hem hata hemde execute ettiğimiz
// context ile beraber bize dönen metodun kullanım şekli..
Policy
.Handle<SomeExceptionType>()
.RetryForever(onRetry: (exception, context) =>
{
// ...
});
Wait and retry
Tekrar etmeler arasında belli bir bekleme süresi koymak istediğimiz durumlarda kullanacağımız yöntem.
// Her bir tekrar sonrası hata yakalama işleminden sonra beklenmesini
// istediğimiz süreleri belirtiyoruz.
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
});
// her bir tekrar deneme sonrası beklenmesini istediğimiz süreyi,
// aynı zamanda da yine her bir tekrar deneme sonraı yapmak istediğimiz ekstra bir
// işlem varsa kullanabilceğimiz yöntem.
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
}, (exception, timeSpan) => {
// ...
});
// yukarıdakine ek olarak bize ilgili context i ve yine
// o deneme öncsi bekleme süresini, hatayı verip, ilgili aksiyonu yazmak istediğimiz yöntem.
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
}, (exception, timeSpan, context) => {
// ...
});
// her bir yeniden deneme sonrası almak istediğimiz aksiyonu yazarken
// bize (exception, timeSpan, retryCount, context) bu 4 bilgiyi de sağlayan yöntem
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
}, (exception, timeSpan, retryCount, context) => {
// ...
});
// Her bir bekleme süresini algoritmik bir sürece bağlamak
// istediğimiz zaman kullanacak olduğumuz yöntem.
// Aşağıdaki örnek için
// 2 ^ 1 = 2 sn
// 2 ^ 2 = 4 sn
// 2 ^ 3 = 8 sn
// 2 ^ 4 = 16 sn
// 2 ^ 5 = 32 sn
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(5, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
);
// Hem bekleme süresini dinamik belirleyip, hemde sonrasında
// yapmak istediklerimiz için bize, timeSpan, exception ve context i dönen metod.
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(
5,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timeSpan, context) => {
// ...
}
);
// yukarıdakine ek olarak bize birde retryCount u dönen metod.
Policy
.Handle<SomeExceptionType>()
.WaitAndRetry(
5,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timeSpan, retryCount, context) => {
// ...
}
);
Wait and retry forever (until succeeds)
İşlem başarılı olana kadar tekrar ederken herbir tekrar öncesi beklemek istediğimiz ve bu tekrarlar arasında farklı işlemler yapmak istediğimiz durum.
// sürekli bekle ve tekrar et
Policy
.Handle<SomeExceptionType>()
.WaitAndRetryForever(retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
);
// Sürekli bekle ve tekrar et ve her bir tekrar deneme sonrası yapmak
// istediğimizi söylediğimiz yöntem.
Policy
.Handle<SomeExceptionType>()
.WaitAndRetryForever(
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timespan) =>
{
//...
});
// Sürekli bekle ve tekrar et ve her bir tekrar deneme sonrası yapmak
// istediğimizi söylediğimiz yöntem.
Policy
.Handle<SomeExceptionType>()
.WaitAndRetryForever(
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timespan, context) =>
{
//...
});
Yukarıda sadece Polly nin, Fault ve Response Handling ini ve bunları yaparken Retry-Forever-Retry ve Wait-Retry patternlerinin uygulanış şekillerini gördük.
Bunların dışında daha bahsedeğim özellikleri mevuct polly nin. Sadece bu kadar bile bir sonraki projenizde olmazsa olmaz kütüphanelerinizden biri olduğunu düşünmenize yeteceiğini düşünüyorum 🙂
Bir sonraki yazımda görüşmek üzere.
Resilient Network Services Bölüm 6 – Polly
Selamlar,
Bu seri kapsamında sizlere tanıtmak istediğim son kütüphane Polly. Bu kütüphaneyi tanıdıktan sonra bunların hepsini bir arada nasıl kullanabiliriz kısmı ile ilgili yazacağım.
Polly, Resilient Network Service katmanı oluşturmak için en önemli işlerden biri olan resilience and transient-fault-handling dediğimiz kavramı projemizde çok kolay bir şekilde kullanmamızı sağlıyor. Serinin ilk yazısında bahsettiğim gibi, yapılan bir api request in cevabı olarak aldığıımız sonucun neticesinde neler yapmalıyız, tekrar denemeli miyiz, yoksa başka bir yöntem mi uygulamalıyız gibi sorularımız için hem yardımcı metodları mevcut hem de kendi istediğimiz senaryolarıda içerisine dahil etmemize olanak sağlıyor.
Polly target olarak .NetStandard 2.0 ile yazıldığı için,
- .NET Framework 4.6.1
- .NET Core 2.0+
- Mono
- Xamarin
- UWP
target li tüm projelerde çalışabiliyor.
.NetFoundation ın bir parçası olan polly, bir yazılımcının api haberleşmesinde, Retry Pattern, Circuit-Breaker Pattern, Timeout Policy, BulkHead Isolation gibi senaryoları destekleyen extension ları ile, zaman kazanılmasını ve sistemin daha sağlıklı yazılmasını sağlıyor.
Bir xamarin projenizdeki Standard yada Portable projenize yada kullanmak istediğiniz diğer tipteki projelerinizin içinde Polly paketini eklemeniz yeterli.
Install-Package Polly
Yukarıda belirttiğim senaryolar detaylı ve kapsamlı işler olsa da, hem polly nin önerdiği hemde seçerken yardımcı olması adına kısa açıklamalarına bir bakalım.
Retry Policy
Bir api haberleşmesi sırasında bir API den size dönecek bazı hatalar geçici olabilir. Server ın anlık yoğunluktan dolayı geçici olarak kısa süreli verdiği hatalar gibi. Bu durumda belli bir bekleme süresi ile, kullanıcıya farkettirmeden arkada tekrar deneme senaryolarını implemente ederek, kullanıcıya hata göstermekten kaçınabiliriz.
Circuit-Breaker Policy
Bazen yine aşırı yoğunluktan yada bir bug dan dolayı sistemde zamanla meydana gelen yavaşlıklardan ötürü, aldığınız cevaplar uzayabilir. Sistemin gerçek anlamda sıkıntı çektiğini düşündüğünüz durumlarda, kullanıcıya uzun sürelerce loading şeklinde bekletmektense, daha hızlı olarak hata almış gibi işlemi yarıda kesmek, hem uygulamanıza, hem API nin sunucusuna, hem kullanıcıya daha fazla katkı sağlayabilir. Yani bu işlem belli ki şuan sıkıntılı, “ben bu API nin üzerine daha fazla gitmeyeyim, biraz zaman verelim kendini toparlasın” gibi durumlar için düşünebiliriz.
Timeout
Verdiğimiz timeour süresinin sonrasına hiçbir request aşmayacağını garanti eder ve bu sayede istemediğimiz sürekli beklemelerden kullanıcımızı kurtarabiliriz.
Cache
Anlaşılacağı üzere, benzer requstleri atmayıp cache inden geri döndürüyor.
Bulkhead Isolation
Bunu örneklere geçtimizde daha iyi anlayabiliriz ama özetel bu şu senaryo için var. Bir process hata ona bağlı diğer requestlerin-işlemlerin de hata alacağı baştan belli olsa da sistem bunu bilmeyeceği gereksiz request ve işlem yapma isteği hem sunucu hem de client tarafına gereksiz yük bindireceğinden, bu gibi işlemleri belli bir kümede toplayıp bunlara belli kurallar set edebiliyoruz, dolayısı ile sistemin geri kalanından bu gibi durumları izole edip, bir yerde başlayan bir zincirleme hata senaryosunun tüm uygulamayı kitlemesini, zarar vermesini önleyebiliriz.
Fallback
Hatalar oluyor, olucaktır da. Bu gibi durumlarda sistemin hataya düşmeden, ne şekilde bu hataları handle edeceğimizi yazacağımız delegate ler olarak düşünebiliriz.
PolicyWrap
Düzgün bir sistem yazmaya çalışırken, yukarıda bahsettiğim tüm bu senaryoların birleşebileceği durumlara karşılık olarak, Polly nin bize sunduğu bu senaryolara karşılık gelen policy leri bir arada kullanmamıza olanak sağlayan yapı için bunu kullanacağız.
Birçok API ın kullanımında olduğu gibi, Polly de bu özelliklerinin çoğunun kullanımı için size Policy adında bir sınıf vriyor ve bu sınıfı kullanarak ayrı ayrı her bir haberleşme için istediğiniz policyleri kullanmanıza olanak sunuyor. Bir sonraki yazımda tüm bu senaryoların nasıl kullanılıcağına detaylı olarak değineceğim. Şimdilik bu kadar özet bilgi bence yeterli 🙂
Bir sonraki yazımda görüşmek üzere.