REST: come determinare le eccezioni transitorie?

4

Mentre cerco di affrontare la possibilità di un errore quando invoco endpoint REST o in generale qualsiasi endpoint HTTP, mi chiedo se ci siano standard o pattern nelle specifiche HTTP o nel settore per affrontare il problema di determinare quando un'eccezione è transitoria e quando non è particolarmente utile ai fini della retryability.

Poiché un servizio remoto potrebbe fallire in qualsiasi momento, determinare quando dovremmo ritentare un'operazione deve essere una caratteristica fondamentale della nostra architettura distribuita.

  • Ho iniziato considerando prima il metodo idempotency (ad esempio, get, put, delete è sicuro di riprovare in base ai nostri contratti di servizio).
  • Poi ho preso in considerazione il codice di stato dove, ad es. 4xx non vengono riprovati, ma gli errori 5xx possono essere riprovati a seconda del tipo di errore, cioè se è transitorio o meno. Ad esempio, se il servizio remoto utilizza un database e ottengo un errore 5xx causato da un timeout della query, è possibile riprovare poiché la condizione è transitoria e il richiamo del servizio molto probabilmente riuscirà se riprovo. Tuttavia, potrebbero esserci altri tipi di errori che non sono transitori e che preferirei evitare di riprovare. Ad esempio, in passato abbiamo avuto degli errori causati da un DBA che aggiungeva un nuovo vincolo a una tabella e quindi un servizio che stava funzionando prima che iniziasse il fallimento con un errore non transitorio come una violazione del vincolo.

In passato, abbiamo commesso l'errore di ripetere all'infinito un errore di 500 pensando che fossero sempre transitori e che il servizio remoto alla fine si riprendesse e fosse in grado di gestire la richiesta. In particolare nelle interazioni da computer a computer (orchestrazioni) dove vorremmo evitare a tutti i costi la propagazione di un'eccezione poiché richiederebbe complesse transazioni di compensazione e la pubblicazione di richieste parzialmente elaborate in un DLQ per un successivo intervento umano.

In generale mi piacerebbe sapere come le persone del settore di solito si occupano di questo problema, in che modo questa proprietà di "transientness" viene trasmessa dal server al client utilizzando il protocollo HTTP.

Dovrei ricorrere all'utilizzo di codici di stato http personalizzati o dovrei comunicare questa proprietà in un'intestazione o in una proprietà del corpo?

Se esiste una soluzione standard per questo?

Sarebbe fantastico perché, se ci fosse, potrei aspettarmi che i servizi di terze parti consumino i miei servizi sapendo che si comporteranno correttamente e allo stesso tempo significa che potrei anche consumarli senza dover riconfigurare il mio protocollo per il loro particolare implementazione.

Accolgo con favore qualsiasi suggerimento sui protocolli di retryability per progettare buoni contratti di assistenza che mi aiutino a creare buoni cittadini del nostro ecosistema che potrebbero potenzialmente essere integrati con servizi di terze parti in futuro.

Finora sono particolarmente sbalordito nello scoprire che un modello di architettura distribuita come questa non fa della retryability un cittadino di prima classe, ma potrebbe essere che mi sbaglio nella mia interpretazione di come dovrebbe funzionare l'implementazione. Qualcuno può indicarmi la giusta direzione?

    
posta edalorzo 03.02.2017 - 21:46
fonte

2 risposte

4

Per i tuoi fallimenti transitori, non restituire 500 . Restituisci 503 Servizio non disponibile . Il timeout della query è causato da "un sovraccarico temporaneo", non da un errore sottostante nel codice. Nella tua risposta, includi un'intestazione Riprova dopo che indica per quanto tempo il client deve attendere prima di riprovare richiesta.

    
risposta data 05.02.2017 - 14:50
fonte
1

Probabilmente ritentare infinitamente non è una buona idea. Se qualcosa va giù e non si presenta rapidamente, è uno spreco di risorse. Il modo in cui ho gestito questa situazione era avere un numero fisso di tentativi e un ritardo sempre più lungo tra ogni tentativo. Nella mia esperienza, se qualcosa non risponde entro diversi tentativi, sarà probabilmente giù per ore. Ma sono solo le mie esperienze ...

I dettagli di quanto spesso riprovare e la frequenza di attesa per ogni nuovo tentativo dipenderanno probabilmente dal servizio specifico con cui stai interagendo e dovrai solo fare affidamento sulla tua esperienza con quel servizio.

Un servizio a cui la mia applicazione parla occasionalmente, ma i tentativi di solito hanno esito positivo. Non ha mai restituito un codice di errore 5xx (beh, vedo 504 nei log per il timeout, ma mai nessun altro). Finora, non ho mai dovuto riprovare più di 5 volte, aspettando almeno 30 secondi tra ogni tentativo. Il loro server è noto per essere lento, ed è stato sovraccaricato prima. Un altro servizio (di un altro fornitore) risponde sempre molto rapidamente, ma a volte restituisce un codice 5xx (in genere 500 o 503) e un tentativo 1 o 2 di solito risolve il problema. Se qualcosa richiede più della quantità consentita di riprovare, viene registrato come un errore grave e l'applicazione continua a lavorare su altre cose.

Un'altra cosa: se stai scrivendo entrambi il servizio e il client, puoi aggiungere informazioni più dettagliate al messaggio di risposta per un errore 5xx. In questo modo, se sai che il servizio che stai scrivendo può recuperare da alcuni errori ma non da altri, potresti includere quel dettaglio nella risposta in modo che il client possa riprovare o meno. Ad esempio, ho visto un servizio che ha un limite di velocità. Quando viene superato questo limite, la risposta all'errore 5xx viene fornita con un'intestazione "riprova dopo", ovvero il numero di minuti dopo i quali il limite viene reimpostato e quindi è possibile riprovare. In tal caso, un'intestazione personalizzata (che deve essere documentata per altri utenti) contiene informazioni su uno specifico scenario di ripetizione.

    
risposta data 03.02.2017 - 22:14
fonte