Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

di il
26 risposte

Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

Pensate ad una pagina in cui io scrivo "Andrea" e poi premo su "Start" e poco tempo dopo leggo "Ciao Andrea!" nella stessa pagina. Il tempo per una richiesta del genere è piccolissimo ma se le operazioni fossero più lunghe (calcoli, query, ecc...) il sistema potrebbe impiegare 36 secondi (scrivo un numero a caso ovviamente). Se invece di 36 secondi servissero 36 minuti? Se invece di 36 minuti, 36 ore? Cosa accadrebbe? C'è un tempo massimo di attesa? Si può gestire questo tempo massimo? In che modo?

26 Risposte

  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    iBaffiPro ha scritto:


    Se invece di 36 secondi servissero 36 minuti? Se invece di 36 minuti, 36 ore? Cosa accadrebbe? C'è un tempo massimo di attesa? Si può gestire questo tempo massimo? In che modo?
    I timeout esistono ovviamente a livello di networking (e anche HTTP di conseguenza). E la questione è che ci sono timeout preimpostati non solo a livello del "server" ma anche per ciascuno dei vari browser. Per Firefox ad esempio ci sono vari timeout configurabili, quelli che trovi documentati qui https://kb.mozillazine.org/About:config_entries#Networ. denominati "network.****.timeout".
    Quindi i timeout in generale esistono e per ovvi buoni motivi. E sono cose che in generale NON puoi controllare solo dalla tua webapp. Quindi l'obiettivo non dovrebbe essere quello di "controllare" questi timeout ma di ragionare diversamente a livello di architettura.

    Se c'è un lavoro "lungo" (molti secondi ... minuti ... ore) lo si fa eseguire in modo asincrono, ovvero il lavoro gira in "background" sul server e la response arriva subito al client. Magari si può generare un identificativo del lavoro e la pagina potrebbe fare una logica di "polling" ogni tot di tempo per dare l'aggiornamento all'utente.

    Il polling potrebbe essere fatto in automatico (con request asincrone) se fosse possibile usare cose tipo JQuery, Angular, ecc... Se invece l'utilizzo di Javascript non è contemplato, il polling lo potrebbe fare comunque "a mano" l'utente. Ad esempio, alla richiesta del lavoro, si genera es. un UUID e si fa un redirect alla pagina blabla/stato-lavoro.html?id=4a1e93ff-f179-49b6-b680-094fba5746d0 e nella pagina si dice all'utente qualcosa tipo "fai refresh quando vuoi per verificare lo stato del lavoro". Ovviamente lato server servono delle apposite strutture dati per tracciare questi lavori.

    Se i lavori fossero molti e di tipi diversi, si potrebbe anche pensare di fare una gestione persistente dei job, con una tabella su DB che traccia i lavori indicando data/ora di inizio, lo stato, chi l'ha richiesto, ecc... Che sarebbe utile anche come "storico" e per eventuale troubleshooting. E poi predisporre magari un pagina dove l'utente possa vedere lo stato dei lavori.

    Se poi i lavori fossero davvero molti e/o lunghi, tali che una singola macchina non è in grado di gestirli tutti, allora si può ragionare ancora di più a livello di architettura, predisponendo altre macchine su cui far girare i lavori e mettendole in comunicazione con la webapp tramite sistemi di messaggistica tipo RabbitMQ, Apache ActiveMQ, ecc... Si tratta ovviamente di architetture ben più complesse di cui presumo non sai assolutamente nulla e che richiedono ovviamente molte più risorse hw/sw.

    In definitiva, l'obiettivo dovrebbe essere quello di cambiare la strategia di gestione dei lavori "lunghi" .... NON cercare di andare "contro" i timeout!
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    Mi accontento di non dare alcuna informazione all'utente, basta che in un modo o nell'altro si arrivi al termine del calcolo. Riesci a darmi un aiuto altrimenti questo lavoro che ho fatto lo posso buttare nel cestino e ho sprecato inutilmente un sacco di tempo.
    Googlando trovo questo:
    https://www.javaboss.it/processi-asincroni-in-spring
    Può andare?
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    iBaffiPro ha scritto:


    https://www.javaboss.it/processi-asincroni-in-spring
    Può andare?
    Sì, il concetto è quello, usare @Async che però va innanzitutto abilitato con @EnableAsync in una classe @Configuration o sulla classe @SpringBootApplication.

    La documentazione ufficiale:
    (Spring Framework):
    (Spring Boot): Task Execution and Scheduling
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    Ho implementato il codice sul mio programma ma ho nuovi problemi. Se l'utente A avvia un metodo asincrono M1 e si accorge che dopo molto tempo non termina non può interromperlo. Se l'utente A avvia lo stesso metodo ma con parametri meno impegnativi in termini di tempo rispetto ad M1 quest'ultimo non si interrompe. Inoltre, non capisco neppure come interrompere il metodo asincrono M1. La mia configurazione è questa:
    Nel controller:
    
    eseguiTestServer.eseguiAsincrono(utenteAutenticato,testServerDalForm);
    
    Modifica della classe sincrona esegui()
        
        @Async
        public String eseguiAsincrono(Utente utenteAutenticato, TestServer testServer){
            return esegui(utenteAutenticato, testServer);
        }
    
    Nel package:
    
    @Configuration
    @EnableAsync
    public class ConfigurazioneMetodiAsincroni {
    
    }
    
    Al momento esegui() genera come String un tag <p> HTML che restituisce l'output di un calcolo ma con l'uso di @Async pensavo di trasformare esegui() in un metodo void e salvare il tag <p> nel database. Pensavo anche di aggiungere un'altra pagina a cui andare a leggere, quando disponibile, il risultato. L'unico problema è che non so come bloccare il metodo esegui() lanciato da un preciso utente. Inoltre non so cosa accadrebbe se oltre ad un utente A che lancia eseguiAsincrono() si aggiungesse un utente B, un utente C, ecc...
    Con @Async posso far girare il metodo in background ma non posso interromperlo, senza @Async posso interrompere il metodo ma non posso farlo girare in background.
    Qui c'è qualche indicazione ma non si capisce la soluzione:
    https://stackoverflow.com/questions/38880069/spring-cancel-async-task
    Qui c'è altro:
    https://www.baeldung.com/spring-asyn
    ma non si chiarisce il discorso dell'interruzione del metodo.
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    iBaffiPro ha scritto:


    Con @Async posso far girare il metodo in background ma non posso interromperlo, senza @Async posso interrompere il metodo ma non posso farlo girare in background.
    Se avessi letto la documentazione (quella linkata da me) dovresti aver visto che a parte mettere il metodo come void (caso particolare se non c'è proprio bisogno di alcun risultato), lo scenario tipico è di mettere come tipo di ritorno un Future<T> (ma è anche accettato il ListenableFuture di Spring e il CompletableFuture di JDK 8+). E poi nel metodo far ritornare materialmente un AsyncResult.

    Future ha un cancel(boolean). Se fai cancel(true), il task viene flaggato per l'interruzione usando il meccanismo della interruption (metodo interrupt() di java.lang.Thread). Questo però è solo un "flag", cosa succeda alla interruzione DIPENDE da cosa sta facendo il task in quel momento. Quindi bisogna vedere quali operazioni fa il task (I/O? computazioni? ecc..). L'obiettivo finale è di rendere il task "cooperativo" alla interruzione affinché la interruzione sia rapida ed efficace. Se il task non fosse cooperativo in questo senso, la interruzione potrebbe avvenire solo dopo molto tempo o, peggio, addirittura non avvenire affatto.

    Quante nozioni hai su questi aspetti dei thread? Ancora una volta te lo ripeto, le "grane" che hai con Spring non dipendono tanto, secondo me, da Spring in sé ma dalla mancanza di nozioni molto più basilari.
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    Ho provato a pacioccare con il metodo Future ma non sono arrivato a nulla. Non è più semplice ragionare nel modo seguente?
    Creo un thread con il nome dell'utente loggato. Se il thread con quel nome è già attivo lo interrompo e lo riavvio altrimenti lo avvio semplicemente. Se il thread richiede troppo tempo lo interrompo semplicemente premendo su un altro pulsante della stessa pagina.
    Ho provato a scrivere questo codice ma se dalla pagina html rifaccio partire il metodo eseguiAsincrono() il nuovo test non parte.
    
    @Service
    public class EseguiTestServerAsincrono {
    
        @Autowired
        EseguiTestServer eseguiTestServer;
    
        @Async
        public void eseguiAsincrono(Utente utenteAutenticato, TestServer testServer, String azione){
            try {
                if(azione.equals("avvia")){
                    try{
                        Thread threadAvvia = new Thread(){
                            public void run() {
                                eseguiTestServer.esegui(utenteAutenticato, testServer);
                            }
                        };
                        threadAvvia.setName(utenteAutenticato.getNome());
                        if(threadAvvia.isAlive()) {
                            threadAvvia.interrupt();
                            threadAvvia.start();
                        }else {
                            threadAvvia.start();
                        }
                    }catch (Exception e){
                        System.out.println("Non è stato possibile eseguire il test.");
                        System.out.println(e.getCause());
                        System.out.println(e.getMessage());
                    }
                }else if(azione.equals("arresta")){
                    try{
                        Thread threadAvvia = new Thread(){
                            public void run() {
                                synchronized(this) {
                                    while (!Thread.currentThread().isInterrupted()) {
                                        eseguiTestServer.esegui(utenteAutenticato, testServer);
                                    }
                                }
                            }
                        };
                        threadAvvia.setName(utenteAutenticato.getNome());
                        if(threadAvvia.isAlive()) {
                            threadAvvia.interrupt();
                        }
                    }catch (Exception e){
                        System.out.println("Non è stato possibile terminare il test in azione.");
                        System.out.println(e.getCause());
                        System.out.println(e.getMessage());
                    }
                }else{
                    System.out.println("Non è chiara l'operazione che si desidera compiere.");
                }
            }catch (Exception e){
                System.out.println("Non è stato possibile eseguire l'azione richiesta.");
                System.out.println(e.getCause());
                System.out.println(e.getMessage());
            }
        }
    
    }
    
    
    In metodo esegui(), quello che voglio rendere asincrono, esegue diverse operazioni:
    1) query (lettura/modifica/scrittura)
    2) calcoli
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    Non puo' funzionare:
    puoi avere anche 100 thread con lo stesso nome!

    Per fare in quel modo DEVI avere un registro GLOBALE in cui registri i tread.
    PRIMA di crearne uno con quel nome, DEVI controllare nel registri se non c'e' gia'.
    Se non c'e', lo creie lo registri, se c'e', sai che c'e'.

    MA NON BASTA: quando il thread termina si deve auto-de-registrare.
    MA NON BASTA: devi essere sicuro che il nome che stai usando si riferisce esattamente a quell'utente e non ad un'altro che non centra nulla.
    MA NON BASTA: mi vengono in mente altri 'milioni di casini' ...

    Comunque, se ad uno piace darsi le martellate sulle falangi ...
    chi siamo noi per dire che non e' una buona idea?
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    MA NON BASTA ANCORA!
    Se ho capito bene il codice del thread ogni tanto dovrebbe verificare se è stato interrotto e se sì procedere all'interruzione. interrupt() non interrompe nulla, setta solo un flag della interruzione. In buona sostanza non saprei neppure come "addestrare" esegui() per interrompersi. esegui() non lo voglio editare.
    Quindi migliorabile cosa suggerisci?
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    Esattamente:
    Thread.interrupt()
    SEMPLICEMENTE setta un flag !

    Il codice in esecuzione all'interno del thread (che NON E' il threa stesso!) DEVE controllare, con una certa regolarita', SE il flag e' stato settato, con
    
    Thread.interrupted()
    
    e quindi ""terminare"" in modo ""controllato"".

    Questo ha una sua logica: non puoi ammazzare il thread in un punto qualunque della sua esecuzione!
    Se lo fai, che ne so, mentre stai aggiornando una tabella, se ti va bene, invece di una commit viene fatta una rollback, se ti va male, ti ritrovi con il DB in stato inconsistente!
    Ma ci sono situazioni anche peggiori!
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    Il suggerimento e': devi affrontare il problema in modo piu' ""formale""/""teorico"".

    Se non lo fai, rischi di introdurre piu' ""casini"" che ""soluzioni"".
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    Migliorabile, tu quale strada prenderesti?
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    Questo funziona:
    
    @Service
    public class EseguiTestServerAsincrono {
    
        @Autowired
        EseguiTestServer eseguiTestServer;
    
        @Async
        public void eseguiAsincrono(Utente utenteAutenticato, TestServer testServer, String azione){
            try {
                if(azione.equals("avvia")){
                    try{
                        Thread threadAvvia = new Thread(){
                            public void run() {
                                eseguiTestServer.esegui(utenteAutenticato, testServer);
                            }
                        };
                        threadAvvia.start();
                        Thread.sleep(5000);
                        threadAvvia.interrupt();
                    }catch (Exception e){
                        System.out.println("Non è stato possibile eseguire il test.");
                        System.out.println(e.getCause());
                        System.out.println(e.getMessage());
                    }
                }else if(azione.equals("arresta")){
                    try{
                        //
                    }catch (Exception e){
                        System.out.println("Non è stato possibile terminare il test in azione.");
                        System.out.println(e.getCause());
                        System.out.println(e.getMessage());
                    }
                }else{
                    System.out.println("Non è chiara l'operazione che si desidera compiere.");
                }
            }catch (Exception e){
                System.out.println("Non è stato possibile eseguire l'azione richiesta.");
                System.out.println(e.getCause());
                System.out.println(e.getMessage());
            }
        }
    
    }
    
    ma ha 2 problemi:
    1) bisogna aggiungere in esegui() dei pezzi di codice qua e la per rendere lo stesso metodo collaborativo (penso che non ci sia altra soluzione su questo punto);
    2) non si riesce a bloccare il thread quando si vuole ma solo dopo un tempo prestabilito (5 secondi).
    A me piacerebbe interrompere il metodo esegui() quando l'utente decide di rilanciarlo o semplicemente quando l'utente decide di interromperlo. Inoltre mi piacerebbe che l'utente potesse terminare il proprio metodo esegui(), non tutti quelli lanciati dagli altri utenti.
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    iBaffiPro ha scritto:


    Non è più semplice ragionare nel modo seguente?
    Creo un thread con il nome dell'utente loggato. Se il thread con quel nome è già attivo lo interrompo e lo riavvio altrimenti lo avvio semplicemente. Se il thread richiede troppo tempo lo interrompo semplicemente premendo su un altro pulsante della stessa pagina.
    No mi spiace, quello che hai scritto è praticamente quasi tutto senza alcun senso. Ci sono oltretutto vari aspetti che non hai minimamente considerato.
    Innanzitutto nelle webapp sarebbe meglio non gestire i thread per conto proprio (cioè direttamente con java.lang.Thread). Primo: perché già il contesto è altamente multi-thread, con il server che crea già di suo decine di "worker" thread in cui gestire le request; secondo: bisogna comunque anche pensare alla terminazione/undeploy della applicazione. Quando viene fatto l'undeploy della webapp (o il server viene stoppato), l'application context di Spring va "giù" e siccome l'Executor Service usato per gestire i task @Async è agganciato al ciclo di vita del contesto, viene fatto lo shutdown dell'Executor Service in modo appropriato.
    Se invece gestisci tu "a mano" un Thread e non presti attenzione a questo aspetto, rischi di lasciare in vita quel tuo Thread e la webapp può non risultare undeployabile o il server non stopparsi. Ricorda, in generale, che una JVM termina solo quando l'ultimo "user" (non-daemon) thread termina.

    Inoltre c'è una cosa che probabilmente non sai neanche. Dopo che hai istanziato un java.lang.Thread, fai lo start(). Il run() del thread quindi farà la sua vita e infine termina. Una volta terminato, NON puoi assolutamente fare uno start() su quello stesso oggetto Thread! È illegale farlo. Bisogna creare un nuovo oggetto Thread.

    Poi c'è comunque una cosa: se fai interrupt() NON c'è alcuna garanzia (ripeto: DIPENDE da cosa fa il task in quel momento), che appena immediatamente dopo l'interrupt() il thread sia già davvero terminato!

    Insomma stai "sorvolando" su talmente tante questioni, che non si può nemmeno tentare di fare una qualche "correzione" al tuo codice. Quello che continuo a ripeterti è che secondo me dovresti davvero fare un percorso (di studio, chiaramente) per acquisire bene le basi.

    Ti faccio un paragone: immagino che tu non sia capace di scalare le montagne (se fosse invece sì, beh ottimo, chiaro). È come se qualcuno ti portasse in elicottero a metà dell'Everest e ti lascia lì. Ma è chiaro che non sapendo scalare, faresti fatica e non riusciresti bene a salire oltre. E probabilmente avresti anche timore e difficoltà a scendere. Ecco: il tuo utilizzo attuale di Java/Spring/Spring Boot è proprio uguale a questo.

    Pensaci un po' a quanto ho appena detto.
  • Re: Ci sono dei limiti di attesa per l'output di una pagina web? Come edito questi limiti con Spring Boot 2?

    Però trovarsi a metà dell'Everest e lasciarsi morire non è neppure la soluzione migliore.
    Se lascio tutto così sono costretto a buttare la WebApp nel cestino, è inutilizzabile.
Devi accedere o registrarti per scrivere nel forum
26 risposte