Retry Pattern dotnetcore

in Architetture Software

Retry Pattern in Dotnet.Core

Reading Time: 2 minutes

Uno dei punti cruciali dello sviluppo di applicazioni basate su micro-services è sicuramente la comunicazione tra servizi. Prima o poi ci troveremo a dover gestire malfunzionamenti temporanei, latenza o semplicemente servizi che non rispondono. Ovviamente, il primo oggetto che dovrà essere configurato sarà l’orchestratore, che consentirà di gestire la giusta scalabilità ed affidabilità dei servizi. Inoltre, molto spesso questi “disservizi” sono temporanei e si risolvono automaticamente (o con l’aiuto di qualche “oggetto” esterno).

Come sviluppatori di codice non è però auspicabile non gestire questo particolare tipo di anomalia.

Riprendiamo quindi alcune considerazioni fatte sull’architettura microservices nel post precedente.

Intro

In un ambiente distribuito e basato su microservices la probabilità di dipendere da un flusso esterno è molto alta: con grande probabilità il flusso sarà di tipo REST, oppure SOA ma in tutti i casi il flusso sarà di tipo request/response. In tutti i casi l’operatività dipenderà dalla risposta.

Supponiamo, ad esempio, di avere un micro-service particolarmente sovraccarico da non poter gestire ulteriori chiamate. Un’altro micro-service che cercherà di contattarlo vedrà rifiutata la richiesta, ma potrebbe ritentare dopo un periodo di timeout, ottenendo finalmente la risposta.

Sicuramente l’utilizzo di code o comunicazione basata su eventi, consente di avere una maggiore risposta ed affidabilità all’intero sistema, anche se potrebbe complicare notevolmente il flusso complessivo.

Nel caso in cui si tratti di servizi HTTP, una soluzione potrebbe essere quella di utilizare sistemi di cache per memorizzare la risposta, anche se va sempre analizzata la vera natura dei dati da recuperare.

Nella documentazione Microsoft viene descritto il Retry Pattern, ovvero una serie di specifiche che consentono di scrivere codice basato su tentativi di richiesta. Questo tipo di approccio può essere sicuramente molto utile, ma potrebbe aumentare la difficoltà di gestione del codice e di manutenzione dello stesso.

Nel tempo sono state implementate alcune librerie specifiche che consentono di ottimizzare e semplificare le operazioni precedenti.

In quest’ambito entrano in gioco particolare librerie che consentono di creare una logica di Retry, Circuit Breaker, Timeout, Bulkhead Isolation e Fallback e di implementarla.

Una di queste librerie è Polly, open-source e perfettamente integrata all’interno di DotNet Core.

Polly

Utilizzando Polly è possibile definire una serie di policy ed eseguire i task all’interno del context :

var myPolicy = Policy
.Handle<HttpRequestException>()
.Or<OperationCanceledException>()
.Retry(3);

abbiamo definito una policy che gestisce HttpRequestExcepion e OperationCanceledException definendo il numero di volte per cui le eccezioni dovranno essere intercettate (nel nostro caso 3 volte).

Per poter eseguire il task all’interno del contesto e con la policy che abbiamo definito:

myPolicy.Execute(()={
   //codice da eseguire
});

Possiamo anche definire un CircuitBreaker:

var myPolicy = Policy
.Handle<SomeExceptionType>()
.CircuitBreaker(6, TimeSpan.FromMinutes(1));

dove viene gestita un’eccezione di tipo “SomeExceptionType” e dopo 6 fallimenti di esecuzione, l’esecuzione del task verrà sospesa per 1 minuto.

Utilizzando la logica delle policy è possibile combinarne più di una utilizzando la funzione wrap:

var resultPolicy = Policy.Wrap(erPolicy, cbPolicy);

Riassumento, Polly consente di eseguire Task assegnandogli una serie di policy che ne consento la gestione in maniera precisa e a prova di failure.