Gestire errori temporanei in una comunicazione gRPC con ASP.NET Core 6

di Morgan Pizzini, in ASP.NET Core,

Il punto focale di una comunicazione gRPC è la creazione di un canale di comunicazione, il GrpcChannel. Attraverso questo canale passeranno tutte le richieste che il client invierà al server, e di conseguenza le risposte inoltrate dal server verso il chiamante.

Il GrpcChannel non è però un oggetto perfetto, tutt'altro: contrariamente a quanto avviene in una comunicazione WebAPI, in cui il ciclo di vita inizia nel momento della chiamata e termina alla ricezione della risposta, il canale di comunicazione gRPC può essere mantenuto attivo per più chiamate, e deve supportare una comunicazione basata sullo stream di dati, nella quale il server, o il client, inviano periodicamente nuovi dati all'interno della stessa chiamata.

Questa modalità di trasferimento di informazioni rende il GrpcChannel soggetto a errori temporanei, o Transient fault, che possono, anche per pochi istanti, interrompere la comunicazione con il server, andando ad invalidare tutte le chiamate in corso. Errori dovuti principalmente a perdita di connessione, indisponibilità del servizio o server timeout.

Fino ad ASP.NET Core 5 l'unico meccanismo di protezione verso questo tipo di errori, era l'implementazione di una logica basata sulla gestione di un'eccezione di tipo RpcException, come mostrato nel seguente codice:

var client = new Greeter.GreeterClient(channel);
try
{
    var response = await client.SayHelloAsync(
        new HelloRequest { Name = "Morgan" });

    Console.WriteLine(response.Message);
}
catch (RpcException ex)
{
    // Logica di retry
}

Questo metodo richiedeva però la duplicazione di molto codice all'interno dell'applicazione: ovunque si fosse instaurato un canale di comunicazione avremmo dovuto gestire l'errore.

In ASP.NET Core 6 il canale supporta una logica di retry automatica basata su una configurazione, iniettata nel momento della creazione del canale.

var defaultMethodConfig = new MethodConfig
{
    Names = { MethodName.Default },
    RetryPolicy = new RetryPolicy
    {
        MaxAttempts = 5,
        InitialBackoff = TimeSpan.FromSeconds(1),
        MaxBackoff = TimeSpan.FromSeconds(5),
        BackoffMultiplier = 1.5,
        RetryableStatusCodes = { StatusCode.Unavailable }
    }
};

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
    ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
});

La configurazione permette il settaggio di vari parametri:

  • MaxAttemps: numero massimo di tentativi di retry
  • InitialBackoff: tempo di attesa per il primo retry
  • BackoffMultiplier: moltiplicatore del tempo di attesa dopo il primo retry
  • MaxBackoff: tempo massimo di attesa prima di retry, mette un limite al valore che il moltiplicatore può generare
  • RetryableStatusCodes: occasione nella quale eseguire la logica di retry

Il codice mostrato all'inizio non necessiterà più del blocco try/catch, venendo così riscritto:

var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(
    new HelloRequest { Name = "Morgan" });

Console.WriteLine(response.Message);

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi