RISOLTO [Java] Gestione dell'eccezione java.net.SocketTimeoutException: Read timed out

Stato
Discussione chiusa ad ulteriori risposte.

Scripta14

Nuovo Utente
95
8
Buongiorno a tutti,

avrei bisogno alcuni suggerimenti per comprendere meglio la gestione dell'eccezione indicata nell'oggetto. Premetto che ho già settato i vari timeout come riportato dal codice a 60000 millisecondi.
Codice:
OkHttpClient httpClient = apiClient.getHttpClient();
        httpClient.setConnectTimeout(_conn,  TimeUnit.MILLISECONDS);
        httpClient.setReadTimeout(_read,  TimeUnit.MILLISECONDS);
        httpClient.setWriteTimeout(_write,  TimeUnit.MILLISECONDS);

Ma può accadere, che a seguito di una chiamata ad un web service che mi restituisce delle informazioni oppure dei file, se c'è un elevato traffico oppure il Server ha problemi di varia natura, posso riscontrare l'eccezione.
Vorrei capire se c'è la possibilità di rieseguire immediatamente e per un certo numero di volte la stessa chiamata al web service anche se scatta l'eccezione. Se riuscite darmi qualche suggerimento da adattare al mio codice è ben gradito.
Scusate la banalità della domanda, ma non è un argomento del tutto conosciuto e mi rivolgo a voi che avete più esperienza.

Grazie in anticipo a tutti.
 

icox

Utente Attivo
497
246
Cosi, senza sapere come hai implementato il resto, potresti pensare di usare un try/catch per gestire le operazioni del socket ed un normale ciclo che itera n volte dove n e' il numero di tentativi che vuoi fare in caso di errori. Raggiunto tale limite il ciclo si interrompe e solleva un eccezione "finale" gestita ad un livello superiore che a quel punto sa che l'operazione e' stata tentata n volte senza successo. Potrebbe inoltre essere una buona idea inserire un timer/sleep (non mi ricordo come si chiama in java) fra un tentativo e l'altro.

Questo al netto di eventuali costrutti gia' implementati in Java, una sorta di "retry", che quindi sarebbero probabilmente da preferire a soluzioni fatte a mano. Su questo pero' non so aiutarti, aspetta qualcuno piu' esperto in Java.
 

Scripta14

Nuovo Utente
95
8
Ti ringrazio per la risposta e per il suggerimento.Non riporto il codice perché sarebbe troppo lungo. Mi serve solo farmi un'idea delle possibili soluzioni che posso utilizzare per gestire al meglio l'eccezione SocketTimeout. Siccome mi hanno passato le classi e i metodi che fanno le chiamate al Web Service, pensavo e speravo di poter gestire gli n-tentativi direttamente sul metodo che veicola l'informazione e ne riceve l'esito della chiamata, ma a quel punto è già stata lanciata l'eccezione e non riesco più a rilanciare il metodo. Quindi mi pare di capire che devo intervenire sulle classi e metodi dove viene costruita la chiamata. Correggimi se sbaglio.
La mia esigenza nasce dal fatto che posso riscontrare più SocketTimeoutException consecutivi e in uno di questi tentativi di invio dell'informazione, nonostante l'eccezione, viene acquisita dal server. La mia domanda è la seguente:
C'è un modo per controllare se il server ha ricevuto qualcosa anche a seguito della SocketimeoutException, evitando di inoltrare nuovamente quanto già inviato?
Grazie a tutti
 

icox

Utente Attivo
497
246
Dipende come sono implementate le classi/librerie che stai usando, puoi intervenire direttamente su quelle oppure creare un tuo wrapper che gestisca l'eccezione. In entrambi i casi devi comunque intercettare l'eccezione, ricreare il socket e quindi inoltrare nuovamente la richiesta.
Il primo caso potrebbe essere utile per estendere la libreria attuale (magari riutilizzabile altrove), implementando appunto un sistema di riconnessione/gestione timeout, nel secondo caso avresti una separazione piu' netta della logica che gestisce il socket (e invia/riceve dati) e quella che si occupa degli errori, con il vantaggio di poter eseguire operazioni specifiche per il contesto in cui ti trovi.

Quanto al secondo punto, il server lo controlli tu? In questo caso puoi implementare una procedura che ignori un dato gia' inoltrato (e.g se qualcosa viene salvato in un db), in caso contrario l'unico modo che hai per saperlo e' interrogare il server per sapere se una certa risorsa e' gia stata ricevuta o una certa operazione eseguita.
IMHO in generale un buon approccio per questi problemi e' considerare un'azione eseguita solo se la chiamata ritorna correttamente, in tutti gli altri casi (disconnessione del client, errore del server, etc) e' da considerarsi non valida e quindi da ritrasmettere, in genere bastano pochi controlli lato server e semplifica la logica.
 

Scripta14

Nuovo Utente
95
8
Grazie per la risposta e sugli interessanti spunti che mi hai dato. Per quanto riguarda la prima parte, come scritto precedentemente utilizzo una serie di classi e metodi che mi sono stati forniti. Diciamo che l'eccezione viene intercettata direttamente dal metodo che gestisce la chiamata, poi io gestisco l'eccezione dove viene lanciato il metodo che serve per passare i parametri per costruire la chiamata e che successivamente lì passa al metodo che invia il tutto.
Questo il metodo che esegue la chiamata:
Codice:
 /**
     * Execute HTTP call and deserialize the HTTP response body into the given return type.
     *
     * @param returnType The return type used to deserialize HTTP response body
     * @param <T> The return type corresponding to (same with) returnType
     * @param call Call
     * @return ApiResponse object containing response status, headers and
     *   data, which is a Java object deserialized from response body and would be null
     *   when returnType is null.
     * @throws ApiException If fail to execute the call
     */
    public <T> ApiResponse<T> execute(Call call, Type returnType) throws ApiException {
        try {
            Response response = call.execute();
            T data = handleResponse(response, returnType);
            return new ApiResponse<T>(response.code(), response.headers().toMultimap(), data);
        } catch (IOException e) {
            throw new ApiException(e);
        }
    }
Pensavo di poter gestire una sorta di loop, in modo da poter lanciare n-volte la chiamata, ma da quello che mi sembra di aver capito, devo intercettare l'eccezione e rieseguire questa chiamata.
Purtroppo non ho il controllo del server, è gestito da soggetti terzi. Quindi ho provato unicamente a settare i metodi della classe OkHttp, ma ho visto che se il problema è sul server, i parametri di timeout modificati non influenzano la chiamata tramite web service.
Anche in questo caso, mi sembra di capire che a parte dei controlli lato client, messi già in atto per evitare una doppia richiesta della stessa informazioni, non si possa fare altro se non intervenire lato server. Corretto?

Grazie ancora per l'aiuto di tutti quelli che vorranno.
 

icox

Utente Attivo
497
246
Puoi eseguire n-volte la chiamata wrappando quel try/catch in un ciclo, ad esempio un while che esegue finche' n > 0, dove n (intero maggiore di 0) viene decrementato di 1 ad ogni 'catch' e tira ApiException() quando arriva a 0. Tale ciclo puoi anche metterlo nel metodo che chiama quella execute(), il funzionamento sarebbe analogo.

C'e' pero' un altro aspetto da considerare e qui entriamo un po' nello specifico, non vorrei dire castronerie (Java non lo uso da un po'), magari dai una controllata sulla documentazione o aspetta qualcuno piu' preparato. Cosa ne e' del socket in seguito a quella eccezione? Nel caso in cui persista nessun problema, ma se andasse ricreato allora non ti basterebbe il loop nel metodo (o in quello che lo chiama) ma devi anche ristabilire la connessione prima di eseguire la chiamata.

Infine, se non hai modo di vedere il server c'e' poco da fare: mi sembra logico, come client, ripetere la chiamata nel caso in cui questa non termini correttamente a causa di un errore di connessione. Il server implementera' opportuni controlli per questi casi (si spera :) )
 
  • Mi piace
Reazioni: Scripta14

Andretti60

Utente Èlite
6,440
5,091
Altro consiglio: a meno di non eseguire quel codice in un thread separato da quello principale, un timeout di un minuto e' decisamente troppo lungo, questo mette il computer irresponsivo per troppo tempo. Sempr emeglio iniziare con un ritardo breve (dipende dalla applicazione) in modo da potere "avvisare" l'utente con un messaggio (tipo "server busy") e richiamare usando timeout sempre piu' lunghi, dando all'utente la possibilita' di interrompere l'operazione.
Certo, devi operare in un ciclo, da ripetere se (e solo se) l'errore che ricevi e' timeout expired. Ma, ripeto, un minuto e' troppo lungo. Se il server non risponde in un minuto, non vedo il motivo di continuare a tartassarlo (un server non dovrebbe mai avere un timeout cosi' lungo) e lascerei all'utente la scelta di cosa fare (retry/cancel)
 
  • Mi piace
Reazioni: Scripta14

Scripta14

Nuovo Utente
95
8
Grazie ad entrambi per i preziosi suggerimenti. Ne approfitto per andare nello specifico di entrambe le vostre risposte, magari mi riuscite a spiegare nel pratico se è corretto le modifiche che posso apportare in base ai vostri suggerimenti.
Codice:
public <T> ApiResponse<T> execute(Call call, Type returnType) throws ApiException {
        int n=5;
        while(n>0) {
        try {
            Response response = call.execute();
            T data = handleResponse(response, returnType);
           return new ApiResponse<T>(response.code(), response.headers().toMultimap(), data);
        } catch(SocketTimeoutException e) {
            n--;
            if(n==0) throw new ApiException(e);
        }
           catch (IOException e) {
            throw new ApiException(e);
        }
        }    
        return null;
    }
Ho provato a wrappare il codice come avete suggerito. Innanzitutto è corretto? Dite che è sufficiente operare in questo modo? Ho dovuto aggiungere la condizione di return NULL, perché con l'eccezione Socket Eclipse mi impone di mettere un return, oppure modificare il metodo con il void.

Andretti60 hai ragione sul tempo di timeout. Siccome l'applicazione deve eseguire un ciclo con più chiamate per ottenere differenti informazioni. Era stato impostato così lungo, perché lato server i tempi di risposta erano lunghi, anche 35-40s per ogni chiamata, e più delle volte,mantenendo il timeout di default pari a 10s mi restituiva il timeout exception. Adesso, lato server la risposta è stata ottimizzata e conseguentemente ottimizzerò lato client.
 

icox

Utente Attivo
497
246
Ciao, si cosi dovrebbe funzionare non vedo grosse problematiche, scrivi un paio di test per sicurezza.
Quel return null finale non e' il massimo (preferenza personale eh! niente di funzionale) ma se il linter fa storie lascialo pure. Al limite se preferisci puoi sostituirlo con un'eccezione per rendere piu' chiaro il fatto che se arrivi li sei in una condizione di errore.
Il timeout/delay fra una richiesta e l'altra? Se il server non risponde solitamente e' buona norma attendere anziche' bombardarlo di richieste (che al 99% finiranno scartate in quanto sovraccarico e/o non raggiungibile...)
 
  • Mi piace
Reazioni: Scripta14
Stato
Discussione chiusa ad ulteriori risposte.

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!