Come funzionano i servlet? Istantanea, sessioni, variabili condivise e multithreading


1144

Supponiamo che io abbia un server web che contiene numerosi servlet. Per informazioni che passano tra quei servlet sto impostando variabili di sessione e di istanza.

Ora, se 2 o più utenti inviano richiesta a questo server, cosa succede alle variabili di sessione?
Saranno tutti comuni per tutti gli utenti o saranno diversi per ciascun utente?
Se sono diversi, come è stato in grado il server di distinguere tra utenti diversi?

Un'altra domanda simile, se ci sono nutenti che accedono a un determinato servlet, allora questo servlet viene istanziato solo la prima volta che il primo utente vi ha effettuato l'accesso o viene istanziato separatamente per tutti gli utenti?
In altre parole, cosa succede alle variabili di istanza?

Risposte:


1821

ServletContext

All'avvio del contenitore servlet (come Apache Tomcat ), verrà distribuito e caricato tutte le sue applicazioni Web. Quando viene caricata un'applicazione Web, il contenitore servlet crea ServletContextuna volta e la mantiene nella memoria del server. La web app web.xmle tutti inclusi web-fragment.xmlviene analizzato i file, e ciascuno <servlet>, <filter>e <listener>hanno trovato (o ogni classe annotati con @WebServlet, @WebFiltere @WebListenerrispettivamente) viene istanziato una volta e mantenute in memoria del server pure. Per ogni filtro istanziato, il suo init()metodo viene richiamato con un nuovo FilterConfig.

Quando a Servletha un valore <servlet><load-on-startup>o @WebServlet(loadOnStartup)maggiore di 0, il suo init()metodo viene anche richiamato durante l'avvio con un nuovo ServletConfig. Tali servlet sono inizializzati nello stesso ordine specificato da quel valore ( 1è il 1o, 2è il 2o, ecc.). Se lo stesso valore è specificato per più di una servlet, allora ogni servlet viene caricato nello stesso ordine in cui appaiono nella web.xml, web-fragment.xmlo @WebServletclassloading. Nel caso in cui il valore "load-on-startup" sia assente, il init()metodo verrà invocato ogni volta che la richiesta HTTP raggiunge quel servlet per la prima volta.

Al termine del contenitore servlet con tutte le fasi di inizializzazione sopra descritte, ServletContextListener#contextInitialized()verrà richiamato.

Quando gli chiude servlet container verso il basso, esso scarica tutte le applicazioni web, invoca il destroy()metodo per tutti i suoi servlet e filtri inizializzati, e tutti ServletContext, Servlet, Filtere Listeneristanze vengono cestinati. Alla fine ServletContextListener#contextDestroyed()verrà invocato.

HttpServletRequest e HttpServletResponse

Il contenitore servlet è collegato a un server Web che ascolta le richieste HTTP su un determinato numero di porta (la porta 8080 viene generalmente utilizzata durante lo sviluppo e la porta 80 in produzione). Quando un client (ad esempio, utente con un browser web, o di programmazione utilizzandoURLConnection ) invia una richiesta HTTP, il contenitore di servlet crea nuovi HttpServletRequeste HttpServletResponseoggetti e li passa attraverso qualsiasi definita Filterdella catena e, infine, l' Servletistanza.

Nel caso dei filtri , doFilter()viene invocato il metodo. Quando il codice del contenitore servlet chiama chain.doFilter(request, response), la richiesta e la risposta continuano al filtro successivo oppure premere il servlet se non ci sono filtri rimanenti.

Nel caso dei servlet , service()viene invocato il metodo. Per impostazione predefinita, questo metodo determina quale dei doXxx()metodi da richiamare in base request.getMethod(). Se il metodo determinato è assente dal servlet, viene restituito un errore HTTP 405 nella risposta.

L'oggetto richiesta fornisce l'accesso a tutte le informazioni sulla richiesta HTTP, come URL, intestazioni, stringa di query e corpo. L'oggetto response offre la possibilità di controllare e inviare la risposta HTTP nel modo desiderato, ad esempio, consentendo di impostare le intestazioni e il corpo (di solito con contenuto HTML generato da un file JSP). Una volta eseguito il commit e il completamento della risposta HTTP, gli oggetti richiesta e risposta vengono riciclati e resi disponibili per il riutilizzo.

HttpSession

Quando un client visita la webapp per la prima volta e / o HttpSessionviene ottenuto per la prima volta tramite request.getSession(), il contenitore servlet crea un nuovo HttpSessionoggetto, genera un ID lungo e univoco (che è possibile ottenere session.getId()) e lo memorizza nel server memoria. Il contenitore servlet imposta anche Cookiea Set-Cookienell'intestazione della risposta HTTP con JSESSIONIDcome nome e l'ID sessione univoco come valore.

Secondo la specifica del cookie HTTP (un contratto a cui devono aderire qualsiasi browser Web e server Web decente), il client (il browser Web) è tenuto a rispedire questo cookie nelle richieste successive Cookienell'intestazione fintanto che il cookie è valido ( cioè l'ID univoco deve fare riferimento a una sessione non scaduta e il dominio e il percorso sono corretti). Utilizzando il monitoraggio del traffico HTTP integrato nel browser, è possibile verificare che il cookie sia valido (premere F12 in Chrome / Firefox 23+ / IE9 + e controllare la scheda Rete / Rete ). Il contenitore servlet controllerà l' Cookieintestazione di ogni richiesta HTTP in entrata per la presenza del cookie con il nome JSESSIONIDe utilizzerà il suo valore (l'ID sessione) per ottenere l'associato HttpSessiondalla memoria del server.

Le HttpSessionrimane attivo fino stato inattivo (cioè non utilizzato in una richiesta) per più del valore di timeout specificato <session-timeout>, un'impostazione web.xml. Il valore di timeout viene impostato automaticamente su 30 minuti. Pertanto, quando il client non visita l'app Web per un periodo di tempo superiore a quello specificato, il contenitore servlet trascina la sessione. Ogni richiesta successiva, anche con il cookie specificato, non avrà più accesso alla stessa sessione; il contenitore servlet creerà una nuova sessione.

Sul lato client, il cookie di sessione rimane attivo fino a quando l'istanza del browser è in esecuzione. Pertanto, se il client chiude l'istanza del browser (tutte le schede / finestre), la sessione viene spostata sul lato client. In una nuova istanza del browser, il cookie associato alla sessione non esisterebbe, quindi non verrebbe più inviato. Ciò provoca la HttpSessioncreazione di un tutto nuovo, con l'utilizzo di un cookie di sessione completamente nuovo.

In poche parole

  • La ServletContextvita finché dura l'app web. È condiviso tra tutte le richieste in tutte le sessioni.
  • Le HttpSessionvite per tutto il tempo come il client interagisce con il web app con la stessa istanza del browser, e la sessione non è scaduta al lato server. È condiviso tra tutte le richieste nella stessa sessione.
  • L' HttpServletRequeste HttpServletResponsediretta dal momento in cui la servlet riceve una richiesta HTTP da parte del cliente, fino a quando la risposta completa (la pagina web) è arrivato. E ' non è condivisa altrove.
  • Tutti Servlet, Filtere Listenerle istanze vivere fintanto che l'applicazione web vive. Sono condivisi tra tutte le richieste in tutte le sessioni.
  • Qualsiasi attributeche è definita in ServletContext, HttpServletRequeste HttpSessionvivrà fino a quando l'oggetto in questione vite. L'oggetto stesso rappresenta lo "scopo" nei framework di gestione dei bean come JSF, CDI, Spring, ecc. Tali framework memorizzano i loro bean con scope come uno attributedei suoi scopi di corrispondenza più vicini.

Sicurezza del filo

Detto questo, la tua principale preoccupazione è probabilmente la sicurezza del thread . Ora dovresti sapere che servlet e filtri sono condivisi tra tutte le richieste. Questa è la cosa bella di Java, è multithread e diversi thread (leggi: richieste HTTP) possono usare la stessa istanza. Altrimenti sarebbe troppo costoso ricrearli init()e destroy()loro per ogni singola richiesta.

Dovresti anche capire che non dovresti mai assegnare alcuna richiesta o dati nell'ambito della sessione come variabile di istanza di un servlet o filtro. Sarà condiviso tra tutte le altre richieste in altre sessioni. Quello è non thread-safe! L'esempio seguente illustra questo:

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

Guarda anche:


25
Quindi, quando in qualche modo riesco a scoprire JSessionId che viene inviato a un client, posso rubare la sua sessione?
Toskan,

54
@Toskan: è corretto. È noto come hack di fissazione della sessione . Si noti che questo non è specifico per JSP / Servlet. Anche tutte le altre lingue lato server che mantengono la sessione tramite un cookie sono sensibili, come PHP con PHPSESSIDcookie, ASP.NET con ASP.NET_SessionIDcookie, eccetera. Questo è anche il motivo per cui la riscrittura dell'URL ;jsessionid=xxxcome fanno automaticamente alcuni framework MVC JSP / Servlet è disapprovata. Assicurati solo che l'ID di sessione non sia mai esposto nell'URL o in altri modi nelle pagine Web in modo che l'utente finale ignaro non venga attaccato.
BalusC,

11
@Toskan: assicurati inoltre che la tua app web non sia sensibile agli attacchi XSS. Vale a dire non visualizzare di nuovo alcun input controllato dall'utente in formato senza escape. XSS ha aperto le porte ai modi per raccogliere gli ID di sessione di tutti gli utenti finali. Vedi anche Qual è il concetto generale alla base di XSS?
BalusC,

2
@BalusC, scusami per la mia stupidità. Significa che tutti gli utenti accedono alla stessa istanza di thisIsNOTThreadSafe giusto?
oscura il

4
@TwoThumbSticks 404 viene restituito quando l'intero servlet stesso è assente. 405 viene restituito quando è presente servlet ma il metodo doXxx () desiderato non è implementato.
BalusC,

428

sessioni

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

In breve: il web server rilascia un identificatore univoco a ciascun visitatore alla sua prima visita. Il visitatore deve riportare quell'ID per essere riconosciuto la prossima volta. Questo identificatore consente inoltre al server di separare correttamente gli oggetti di proprietà di una sessione rispetto a quelli di un'altra.

Istantanea servlet

Se il caricamento all'avvio è falso :

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Se il caricamento all'avvio è vero :

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Una volta che è in modalità di servizio e sul groove, lo stesso servlet funzionerà sulle richieste di tutti gli altri client.

inserisci qui la descrizione dell'immagine

Perché non è una buona idea avere un'istanza per client? Pensa a questo: assumerai un tipo di pizza per ogni ordine che è venuto? Fallo e rimarrai senza lavoro in pochissimo tempo.

Tuttavia, comporta un piccolo rischio. Ricorda: questo singolo ragazzo tiene in tasca tutte le informazioni sull'ordine: quindi se non sei cauto sulla sicurezza dei thread sui servlet , potrebbe finire per dare l'ordine sbagliato a un determinato cliente.


26
La tua foto è molto buona per la mia comprensione. Ho una domanda: cosa farà questo ristorante pizzeria quando arriveranno troppi ordini di pizze, aspettate solo un ragazzo della pizza o assumete un altro ragazzo della pizza? Grazie .
zh18,

6
Restituirà un messaggio conto many requests at this moment. try again later
Please_Dont_Bully_Me_SO_Lords

3
I servlet, diversamente dalle persone che consegnano la pizza, possono fare più di una consegna contemporaneamente. Devono solo prestare particolare attenzione a dove scrivono l'indirizzo del cliente, il sapore della pizza ...
bruno

42

La sessione nei servlet Java è la stessa della sessione in altre lingue come PHP. È unico per l'utente. Il server può tenerne traccia in diversi modi come i cookie, la riscrittura dell'URL ecc. Questo articolo del documento Java lo spiega nel contesto dei servlet Java e indica che esattamente come viene mantenuta la sessione è un dettaglio di implementazione lasciato ai progettisti del server. La specifica stabilisce solo che deve essere mantenuta come unica per un utente attraverso più connessioni al server. Consulta questo articolo di Oracle per ulteriori informazioni su entrambe le tue domande.

Modifica Esiste un eccellente tutorial qui su come lavorare con la sessione all'interno dei servlet. Ed ecco un capitolo di Sun sui servlet Java, cosa sono e come usarli. Tra questi due articoli, dovresti essere in grado di rispondere a tutte le tue domande.


Ciò solleva un'altra domanda per me, poiché esiste un solo contesto servlet per l'intera applicazione e possiamo accedere alle variabili di sessione tramite questo servletcontesto, quindi come possono le variabili di sessione essere univoche per ogni utente? Grazie ..
Ku Jon,

1
come si accede alla sessione da servletContext? Non ti riferisci a servletContext.setAttribute (), vero?
opaco b

4
@KuJon Ogni app Web ha un ServletContextoggetto. Quell'oggetto ha zero, uno o più oggetti sessione: una raccolta di oggetti sessione. Ogni sessione è identificata da una sorta di stringa identificativa, come si vede nei cartoni animati su un'altra risposta. Tale identificatore viene tracciato sul client mediante cookie o riscrittura URL. Ogni oggetto sessione ha le sue variabili.
Basil Bourque,

33

Quando il contenitore servlet (come Apache Tomcat) si avvia, leggerà dal file web.xml (solo uno per applicazione) se qualcosa va storto o mostra un errore sulla console lato contenitore, altrimenti verrà distribuito e caricato tutto il web applicazioni usando web.xml (così chiamato come descrittore di distribuzione).

Durante la fase di istanza del servlet, l'istanza servlet è pronta ma non può servire la richiesta del client perché manca con due informazioni:
1: informazioni di contesto
2: informazioni di configurazione iniziale

Il motore servlet crea l'oggetto interfaccia servletConfig incapsulando le informazioni mancanti sopra nel motore servlet chiama init () del servlet fornendo i riferimenti agli oggetti servletConfig come argomento. Una volta eseguito completamente init (), il servlet è pronto per servire la richiesta del client.

D) Nel corso della vita del servlet quante volte si verificano l'istanziazione e l'inizializzazione ??

A) solo una volta (per ogni richiesta del client viene creato un nuovo thread) solo un'istanza del servlet serve un numero qualsiasi della richiesta del client, vale a dire che, dopo aver servito un server di richiesta client, non muore. Attende altre richieste del client, ovvero quale limitazione CGI (per ogni richiesta client viene creato un nuovo processo) viene superata con il servlet (il motore servlet interno crea il thread).

D) Come funziona il concetto di sessione?

A) ogni volta che getSession () viene chiamato sull'oggetto HttpServletRequest

Passaggio 1 : l'oggetto richiesta viene valutato per l'ID sessione in entrata.

Passaggio 2 : se l'ID non è disponibile, viene creato un oggetto HttpSession nuovo di zecca e viene generato l'ID sessione corrispondente (ovvero HashTable), l'ID sessione viene memorizzato nell'oggetto risposta httpservlet e il riferimento dell'oggetto HttpSession viene restituito al servlet (doGet / doPost) .

Passaggio 3 : se non viene creato l'ID oggetto brandnew disponibile per la sessione, l'ID sessione viene prelevato dalla richiesta dell'oggetto di ricerca viene effettuata nella raccolta di sessioni utilizzando l'ID sessione come chiave.

Quando la ricerca ha esito positivo, l'ID sessione viene archiviato in HttpServletResponse e i riferimenti agli oggetti sessione esistenti vengono restituiti a doGet () o doPost () di UserDefineservlet.

Nota:

1) quando il controllo passa dal codice servlet al client, non dimenticare che l'oggetto sessione è trattenuto dal contenitore servlet, ad esempio il motore servlet

2) il multithreading è lasciato alle persone degli sviluppatori servlet per l'implementazione, ad esempio, gestire le richieste multiple del client per non preoccuparsi del codice multithread

Modulo di breve durata:

Un servlet viene creato all'avvio dell'applicazione (viene distribuito sul contenitore servlet) o al primo accesso (in base all'impostazione del caricamento all'avvio) quando viene istanziata la servlet, viene chiamato il metodo init () del servlet quindi il servlet (la sua unica e unica istanza) gestisce tutte le richieste (il suo metodo service () viene chiamato da più thread). Ecco perché non è consigliabile avere alcuna sincronizzazione al suo interno e dovresti evitare le variabili di istanza del servlet quando l'applicazione non viene distribuita (il contenitore del servlet si arresta), viene chiamato il metodo destroy ().


20

Sessioni : cosa ha detto Chris Thompson.

Istanziazione : viene istanziata una servlet quando il contenitore riceve la prima richiesta mappata sulla servlet (a meno che la servlet non sia configurata per il caricamento all'avvio con l' <load-on-startup>elemento in web.xml). La stessa istanza viene utilizzata per soddisfare le richieste successive.


3
Corretta. Altre considerazioni: ogni richiesta ottiene un nuovo thread (o riciclato) da eseguire su quella singola istanza Servlet. Ogni servlet ha un'istanza e probabilmente molti thread (se molte richieste simultanee).
Basil Bourque,

13

La specifica servlet JSR-315 definisce chiaramente il comportamento del contenitore web nei metodi di servizio (e doGet, doPost, doPut ecc.) (2.3.3.1 Problemi di multithreading, Pagina 9):

Un contenitore servlet può inviare richieste simultanee attraverso il metodo di servizio del servlet. Per gestire le richieste, Servlet Developer deve prevedere disposizioni adeguate per l'elaborazione simultanea con più thread nel metodo di servizio.

Sebbene non sia raccomandato, un'alternativa per lo sviluppatore è implementare l'interfaccia SingleThreadModel che richiede al contenitore di garantire che nel metodo di servizio sia presente un solo thread di richiesta alla volta. Un contenitore servlet può soddisfare questo requisito serializzando le richieste su un servlet o mantenendo un pool di istanze servlet. Se il servlet fa parte di un'applicazione Web contrassegnata come distribuibile, il contenitore può conservare un pool di istanze servlet in ciascuna JVM su cui l'applicazione è distribuita.

Per i servlet che non implementano l'interfaccia SingleThreadModel, se il metodo di servizio (o metodi come doGet o doPost inviati al metodo di servizio della classe astratta HttpServlet) è stato definito con la parola chiave sincronizzata, il contenitore servlet non può utilizzare l'approccio pool di istanze , ma deve serializzare le richieste attraverso di esso. Si consiglia vivamente agli sviluppatori di non sincronizzare il metodo di servizio (oi metodi inviati ad esso) in queste circostanze a causa di effetti dannosi sulle prestazioni


2
Cordiali saluti, l'attuale specifica Servlet (2015-01) è 3.1, definita da JSR 340 .
Basil Bourque,


1
Risposta molto ordinata! @tharindu_DG
Tom Taylor

0

Come si evince dalle spiegazioni precedenti, implementando SingleThreadModel , un servlet può essere garantito dalla sicurezza del thread dal contenitore servlet. L'implementazione del contenitore può farlo in 2 modi:

1) Serializzare le richieste (accodamento) a una singola istanza - è simile a un servlet NON implementa SingleThreadModel MA sincronizzando i metodi service / doXXX; O

2) Creazione di un pool di istanze - che è un'opzione migliore e un compromesso tra l'avvio / sforzo di inizializzazione / tempo del servlet rispetto ai parametri restrittivi (tempo di memoria / CPU) dell'ambiente che ospita il servlet.


-1

No. I servlet non sono sicuri per il thread

Ciò consente di accedere a più di un thread alla volta

se vuoi renderlo Servlet come thread sicuro., U può andare per

Implement SingleThreadInterface(i) che è un'interfaccia vuota non c'è

metodi

o possiamo andare per i metodi di sincronizzazione

possiamo rendere l'intero metodo di servizio sincronizzato usando sincronizzato

parola chiave davanti al metodo

Esempio::

public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException

oppure possiamo inserire il blocco put del codice nel blocco sincronizzato

Esempio::

Synchronized(Object)

{

----Instructions-----

}

Sento che il blocco sincronizzato è meglio che realizzare l'intero metodo

sincronizzato

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.