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.