Comment fonctionnent les servlets? Instanciation, sessions, variables partagées et multithreading

1144

Supposons que j'ai un serveur Web qui contient de nombreuses servlets. Pour les informations passant entre ces servlets, je définis les variables de session et d'instance.

Maintenant, si 2 utilisateurs ou plus envoient une requête à ce serveur, qu'advient-il des variables de session?
Seront-ils tous communs à tous les utilisateurs ou seront-ils différents pour chaque utilisateur?
S'ils sont différents, comment le serveur a-t-il pu différencier les différents utilisateurs?

Une autre question similaire, s'il y a des nutilisateurs accédant à un servlet particulier, alors ce servlet n'est instancié que la première fois que le premier utilisateur y accède ou est-il instancié séparément pour tous les utilisateurs?
En d'autres termes, qu'advient-il des variables d'instance?

Ku Jon
la source

Réponses:

1822

ServletContext

Lorsque le conteneur de servlets (comme Apache Tomcat ) démarre, il déploiera et chargera toutes ses applications Web. Lorsqu'une application Web est chargée, le conteneur de servlet crée ServletContextune fois et la conserve dans la mémoire du serveur. L'application Web web.xmlet tous les web-fragment.xmlfichiers inclus sont analysés, et chacun <servlet>, <filter>et <listener>trouvé (ou chaque classe annotée avec @WebServlet, @WebFilteret @WebListenerrespectivement) est instancié une fois et également conservé dans la mémoire du serveur. Pour chaque filtre instancié, sa init()méthode est invoquée avec un nouveau FilterConfig.

Lorsque a Servleta une valeur <servlet><load-on-startup>ou @WebServlet(loadOnStartup)supérieure à 0, sa init()méthode est également invoquée lors du démarrage avec un nouveau ServletConfig. Ces servlets sont initialisés dans le même ordre spécifié par cette valeur ( 1est le 1er, le 22e, etc.). Si la même valeur est spécifiée pour plus d'un servlet, alors chacun de ces servlets est chargé dans le même ordre que dans le web.xml, web-fragment.xmlou @WebServletclassloading. Dans le cas où la valeur "load-on-startup" est absente, la init()méthode sera invoquée chaque fois que la requête HTTP atteindra ce servlet pour la toute première fois.

Lorsque le conteneur de servlet a terminé toutes les étapes d'initialisation décrites ci-dessus, alors le ServletContextListener#contextInitialized()sera invoqué.

Lorsque le conteneur se ferme de servlet vers le bas, il décharge toutes les applications Web, appelle la destroy()méthode de tous ses servlets et les filtres initialisées, et tous ServletContext, Servlet, Filteret les Listenercas sont saccagés. Enfin, le ServletContextListener#contextDestroyed()sera invoqué.

HttpServletRequest et HttpServletResponse

Le conteneur de servlet est attaché à un serveur Web qui écoute les requêtes HTTP sur un certain numéro de port (le port 8080 est généralement utilisé pendant le développement et le port 80 en production). Lorsqu'un client (utilisateur , par exemple avec un navigateur Web ou par programmationURLConnection ) envoie une requête HTTP, le conteneur de servlet crée de nouveaux HttpServletRequestet des HttpServletResponseobjets et les passe par une définition Filterde la chaîne et, éventuellement, l' Servletinstance.

Dans le cas des filtres , la doFilter()méthode est invoquée. Lorsque le code du conteneur de servlet appelle chain.doFilter(request, response), la demande et la réponse continuent au filtre suivant, ou appuyez sur la servlet s'il n'y a plus de filtre.

Dans le cas des servlets , la service()méthode est invoquée. Par défaut, cette méthode détermine la doXxx()méthode à invoquer en fonction de laquelle request.getMethod(). Si la méthode déterminée est absente du servlet, une erreur HTTP 405 est renvoyée dans la réponse.

L'objet de demande donne accès à toutes les informations sur la demande HTTP, telles que son URL, ses en-têtes, sa chaîne de requête et son corps. L'objet de réponse permet de contrôler et d'envoyer la réponse HTTP comme vous le souhaitez, par exemple, en vous permettant de définir les en-têtes et le corps (généralement avec du contenu HTML généré à partir d'un fichier JSP). Lorsque la réponse HTTP est validée et terminée, les objets de demande et de réponse sont recyclés et mis à disposition pour être réutilisés.

HttpSession

Lorsqu'un client visite la webapp pour la première fois et / ou HttpSessionest obtenu pour la première fois via request.getSession(), le conteneur de servlet crée un nouvel HttpSessionobjet, génère un ID long et unique (que vous pouvez obtenir session.getId()) et le stocke dans le serveur Mémoire. Le conteneur de servlet définit également un Cookiedans l'en- Set-Cookietête de la réponse HTTP avec JSESSIONIDcomme nom et l'ID de session unique comme valeur.

Conformément à la spécification de cookie HTTP (un contrat auquel tout navigateur Web et serveur Web décent doit adhérer), le client (le navigateur Web) est tenu de renvoyer ce cookie dans les demandes suivantes dans l'en- Cookietête tant que le cookie est valide ( c'est-à-dire que l'ID unique doit faire référence à une session non expirée et que le domaine et le chemin sont corrects). En utilisant le moniteur de trafic HTTP intégré de votre navigateur, vous pouvez vérifier que le cookie est valide (appuyez sur F12 dans Chrome / Firefox 23+ / IE9 +, et vérifiez l' onglet Net / Réseau ). Le conteneur de servlet vérifiera l'en- Cookietête de chaque requête HTTP entrante pour la présence du cookie avec le nom JSESSIONIDet utilisera sa valeur (l'ID de session) pour obtenir l'associé HttpSessionde la mémoire du serveur.

Le HttpSessionreste en vie jusqu'à ce qu'il soit inactif (c'est-à-dire non utilisé dans une demande) pendant plus que la valeur de temporisation spécifiée dans <session-timeout>, un paramètre dans web.xml. La valeur du délai d'attente est par défaut de 30 minutes. Ainsi, lorsque le client ne visite pas l'application Web plus longtemps que la durée spécifiée, le conteneur de servlet met la session au rebut. Chaque demande ultérieure, même avec le cookie spécifié, n'aura plus accès à la même session; le conteneur de servlet créera une nouvelle session.

Côté client, le cookie de session reste actif aussi longtemps que l'instance de navigateur est en cours d'exécution. Ainsi, si le client ferme l'instance de navigateur (tous les onglets / fenêtres), la session est mise à la poubelle du côté du client. Dans une nouvelle instance de navigateur, le cookie associé à la session n'existerait pas, il ne serait donc plus envoyé. Cela entraîne la HttpSessioncréation d'un tout nouveau, avec un cookie de session entièrement nouveau utilisé.

En un mot

  • La ServletContextvie aussi longtemps que dure l'application Web. Il est partagé entre toutes les demandes de toutes les sessions.
  • La HttpSessionvie dure aussi longtemps que le client interagit avec l'application Web avec la même instance de navigateur et que la session n'a pas expiré côté serveur. Il est partagé entre toutes les demandes d'une même session.
  • Le HttpServletRequestet en HttpServletResponsedirect à partir du moment où le servlet reçoit une demande HTTP du client, jusqu'à ce que la réponse complète (la page Web) soit arrivée. Il n'est pas partagé ailleurs.
  • Tous Servlet, Filteret les Listenerinstances vivent aussi longtemps que l'application Web vit. Ils sont partagés entre toutes les demandes dans toutes les sessions.
  • Tout attributequi est défini dans ServletContext, HttpServletRequestet HttpSessionvivra aussi longtemps que l'objet dans la vie de question. L'objet lui-même représente la «portée» dans les cadres de gestion de bean tels que JSF, CDI, Spring, etc. Ces cadres stockent leurs beans étendus en tant que attributesa portée de correspondance la plus proche.

Sécurité des fils

Cela dit, votre principale préoccupation est peut-être la sécurité des threads . Vous devez maintenant savoir que les servlets et les filtres sont partagés entre toutes les demandes. C'est la bonne chose à propos de Java, il est multithread et différents threads (lire: requêtes HTTP) peuvent utiliser la même instance. Il serait autrement trop cher à recréer, init()et destroy()eux pour chaque demande.

Vous devez également comprendre que vous ne devez jamais affecter de données de portée de demande ou de session en tant que variable d' instance d'un servlet ou d'un filtre. Il sera partagé entre toutes les autres demandes des autres sessions. Ce n'est pas thread-safe! L'exemple ci-dessous illustre ceci:

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.
    } 
}

Voir également:

BalusC
la source
25
Donc, quand je peux en quelque sorte découvrir le JSessionId qui est envoyé à un client, je peux voler sa session?
Toskan
54
@Toskan: c'est exact. Il est connu sous le nom de hack de fixation de session . Veuillez noter que cela n'est pas spécifique à JSP / Servlet. Tous les autres langages côté serveur qui maintiennent la session par un cookie sont également sensibles, comme PHP avec PHPSESSIDcookie, ASP.NET avec ASP.NET_SessionIDcookie, etc. C'est aussi pourquoi la réécriture d'URL avec ;jsessionid=xxxcomme le font automatiquement certains frameworks JSP / Servlet MVC est mal vue. Assurez-vous simplement que l'ID de session n'est jamais exposé dans l'URL ou par d'autres moyens dans les pages Web afin que l'utilisateur final non averti ne soit pas attaqué.
BalusC
11
@Toskan: Assurez-vous également que votre application Web n'est pas sensible aux attaques XSS. Autrement dit, ne réaffichez aucune entrée contrôlée par l'utilisateur sous une forme non échappée. XSS a ouvert des portes sur les moyens de collecter les ID de session de tous les utilisateurs finaux. Voir aussi Quel est le concept général derrière XSS?
BalusC
2
@BalusC, Désolé pour ma stupidité. Cela signifie que tous les utilisateurs accèdent à la même instance de thisIsNOTThreadSafe, n'est-ce pas?
éclipser
4
@TwoThumbSticks 404 est retourné lorsque le servlet lui-même est absent. 405 est renvoyé lorsque la servlet est présente mais que la méthode doXxx () souhaitée n'est pas implémentée.
BalusC
428

Séances

entrez la description de l'image ici entrez la description de l'image ici

En bref: le serveur Web délivre un identifiant unique à chaque visiteur lors de sa première visite. Le visiteur doit rapporter cette pièce d'identité pour qu'il soit reconnu la prochaine fois. Cet identifiant permet également au serveur de séparer correctement les objets appartenant à une session et ceux d'une autre.

Instanciation de servlet

Si le chargement au démarrage est faux :

entrez la description de l'image ici entrez la description de l'image ici

Si le chargement au démarrage est vrai :

entrez la description de l'image ici entrez la description de l'image ici

Une fois qu'il est en mode service et sur le groove, le même servlet fonctionnera sur les demandes de tous les autres clients.

entrez la description de l'image ici

Pourquoi n'est-ce pas une bonne idée d'avoir une instance par client? Pensez-y: embaucherez-vous un pizzaiolo pour chaque commande reçue? Faites cela et vous seriez en faillite en un rien de temps.

Cela comporte cependant un petit risque. Rappelez-vous: ce gars célibataire détient toutes les informations de commande dans sa poche: donc si vous n'êtes pas prudent sur la sécurité des threads sur les servlets , il peut finir par donner le mauvais ordre à un certain client.

Jops
la source
26
Ta photo est très bonne pour ma compréhension. J'ai une question: que fera cette pizzeria lorsque trop de commandes de pizza arriveront, il suffit d'attendre un pizzaiolo ou d'embaucher plus de pizzeria? Merci .
zh18
6
Il retournera un message avecto many requests at this moment. try again later
Please_Dont_Bully_Me_SO_Lords
3
Contrairement aux livreurs de pizza, les servlets peuvent effectuer plusieurs livraisons en même temps. Ils ont juste besoin de faire attention à l'endroit où ils écrivent l'adresse du client, la saveur de la pizza ...
bruno
42

La session dans les servlets Java est identique à la session dans d'autres langages tels que PHP. Il est unique à l'utilisateur. Le serveur peut en assurer le suivi de différentes manières telles que les cookies, la réécriture d'URL, etc. Cet article de documentation Java l' explique dans le contexte des servlets Java et indique que la façon dont la session est maintenue est un détail d'implémentation laissé aux concepteurs du serveur. La spécification stipule uniquement qu'elle doit être maintenue comme unique pour un utilisateur sur plusieurs connexions au serveur. Consultez cet article d'Oracle pour plus d'informations sur vos deux questions.

Modifier Il y a un excellent tutoriel ici sur la façon de travailler avec la session à l' intérieur de servlets. Et voici un chapitre de Sun sur les servlets Java, ce qu'ils sont et comment les utiliser. Entre ces deux articles, vous devriez pouvoir répondre à toutes vos questions.

Chris Thompson
la source
Cela soulève une autre question pour moi, car il n'y a qu'un seul contexte de servlet pour toute l'application et nous avons accès aux variables de session via ce servletcontext, alors comment les variables de session peuvent-elles être uniques à chaque utilisateur? Merci ..
Ku Jon
1
comment accédez-vous à la session depuis le servletContext? Vous ne faites pas référence à servletContext.setAttribute (), n'est-ce pas?
mat b
4
@KuJon Chaque application Web a un ServletContextobjet. Cet objet a zéro, un ou plusieurs objets de session - une collection d'objets de session. Chaque session est identifiée par une sorte de chaîne d'identification, comme on le voit dans les dessins animés sur une autre réponse. Cet identifiant est suivi sur le client par cookie ou par réécriture d'URL. Chaque objet de session a ses propres variables.
Basil Bourque
33

Lorsque le conteneur de servlet (comme Apache Tomcat) démarre, il lit le fichier web.xml (un seul par application) si quelque chose se passe mal ou affiche une erreur sur la console côté conteneur, sinon, il déploiera et chargera tout le Web en utilisant web.xml (ainsi nommé comme descripteur de déploiement).

Pendant la phase d'instanciation de la servlet, l'instance de servlet est prête mais elle ne peut pas répondre à la demande du client car elle manque avec deux informations:
1: informations de contexte
2: informations de configuration initiale

Le moteur de servlet crée un objet d'interface servletConfig encapsulant les informations manquantes ci-dessus dans ce moteur de servlet appelle init () de la servlet en fournissant des références d'objet servletConfig comme argument. Une fois init () complètement exécuté, le servlet est prêt à répondre à la demande du client.

Q) Pendant la durée de vie du servlet, combien de fois l'instanciation et l'initialisation se produisent-elles ??

A) une seule fois (pour chaque demande client, un nouveau thread est créé), une seule instance de la servlet sert un nombre quelconque de la demande client, c'est-à-dire qu'après avoir servi un serveur de demande client ne meurt pas. Il attend les autres demandes client, c'est-à-dire quelle limitation CGI (pour chaque demande client un nouveau processus est créé) est surmontée avec le servlet (le moteur de servlet interne crée le thread).

Q) Comment fonctionne le concept de session?

A) chaque fois que getSession () est appelée sur un objet HttpServletRequest

Étape 1 : l'objet de demande est évalué pour l'ID de session entrant.

Étape 2 : si l'ID n'est pas disponible, un nouvel objet HttpSession est créé et son ID de session correspondant est généré (c'est-à-dire de HashTable) L'ID de session est stocké dans l'objet de réponse httpservlet et la référence de l'objet HttpSession est renvoyée au servlet (doGet / doPost) .

Étape 3 : si aucun nouvel objet de session d'ID disponible n'est créé, l'ID de session est récupéré à partir de la demande d'objet, la recherche est effectuée dans la collection de sessions en utilisant l'ID de session comme clé.

Une fois la recherche réussie, l'ID de session est stocké dans HttpServletResponse et les références d'objet de session existantes sont renvoyées à doGet () ou doPost () de UserDefineservlet.

Remarque:

1) lorsque le contrôle quitte le code de servlet pour le client, n'oubliez pas que l'objet de session est détenu par le conteneur de servlet, c'est-à-dire le moteur de servlet

2) le multithreading est laissé aux développeurs de servlets pour la mise en œuvre, c'est-à-dire, ne gère pas les demandes multiples du client pour ne pas se soucier du code multithread

Forme courte:

Une servlet est créée au démarrage de l'application (elle est déployée sur le conteneur de servlet) ou lors de son premier accès (en fonction du paramètre de chargement au démarrage) lorsque la servlet est instanciée, la méthode init () de la servlet est appelée puis le servlet (sa seule et unique instance) gère toutes les requêtes (sa méthode service () étant appelée par plusieurs threads). C'est pourquoi il n'est pas conseillé d'y avoir de synchronisation, et vous devez éviter les variables d'instance du servlet lorsque l'application n'est pas déployée (le conteneur de servlet s'arrête), la méthode destroy () est appelée.

Ajay Takur
la source
20

Sessions - ce que Chris Thompson a dit.

Instanciation - une servlet est instanciée lorsque le conteneur reçoit la première demande mappée à la servlet (sauf si la servlet est configurée pour se charger au démarrage avec l' <load-on-startup>élément dedans web.xml). La même instance est utilisée pour traiter les demandes suivantes.

Lauri Lehtinen
la source
3
Correct. Réflexion supplémentaire: chaque demande obtient un nouveau thread (ou recyclé) à exécuter sur cette seule instance de servlet. Chaque servlet a une instance et éventuellement plusieurs threads (si plusieurs requêtes simultanées).
Basil Bourque
13

La spécification de servlet JSR-315 définit clairement le comportement du conteneur Web dans les méthodes de service (et doGet, doPost, doPut etc.) (2.3.3.1 Problèmes de multithreading, page 9):

Un conteneur de servlet peut envoyer des demandes simultanées via la méthode de service de la servlet. Pour gérer les demandes, le développeur de servlet doit prendre des dispositions adéquates pour le traitement simultané avec plusieurs threads dans la méthode de service.

Bien que cela ne soit pas recommandé, une alternative pour le développeur consiste à implémenter l'interface SingleThreadModel qui nécessite que le conteneur garantisse qu'il n'y a qu'un seul thread de demande à la fois dans la méthode de service. Un conteneur de servlet peut satisfaire cette exigence en sérialisant les demandes sur un servlet ou en maintenant un pool d'instances de servlet. Si le servlet fait partie d'une application Web qui a été marquée comme distribuable, le conteneur peut conserver un pool d'instances de servlet dans chaque machine virtuelle Java sur laquelle l'application est distribuée.

Pour les servlets n'implémentant pas l'interface SingleThreadModel, si la méthode de service (ou des méthodes telles que doGet ou doPost qui sont envoyées à la méthode de service de la classe abstraite HttpServlet) a été définie avec le mot clé synchronized, le conteneur de servlet ne peut pas utiliser l'approche de pool d'instances , mais doit sérialiser les demandes par son intermédiaire. Il est fortement recommandé aux développeurs de ne pas synchroniser la méthode de service (ou les méthodes qui lui sont envoyées) dans ces circonstances en raison d'effets néfastes sur les performances.

tharindu_DG
la source
2
Pour info, la spécification Servlet actuelle (2015-01) est 3.1, définie par JSR 340 .
Basil Bourque
1
Réponse très soignée! @tharindu_DG
Tom Taylor
0

Comme il ressort clairement des explications ci-dessus, en implémentant le SingleThreadModel , un servlet peut être assuré de la sécurité des threads par le conteneur de servlet. L'implémentation du conteneur peut le faire de 2 manières:

1) Sérialisation des demandes (mise en file d'attente) vers une seule instance - cela est similaire à une servlet qui n'implémente PAS SingleThreadModel MAIS synchronisant les méthodes service / doXXX; OU

2) Création d'un pool d'instances - qui est une meilleure option et un compromis entre l'effort de démarrage / d'initialisation / le temps de la servlet par rapport aux paramètres restrictifs (mémoire / temps CPU) de l'environnement hébergeant la servlet.

Mahesh Balasubramanian
la source
-1

Non. Les servlets ne sont pas thread-safe

Cela permet d'accéder à plusieurs threads à la fois

si vous voulez rendre Servlet sûr pour les threads., U peut opter pour

Implement SingleThreadInterface(i) qui est une interface vierge, il n'y a pas

les méthodes

ou nous pouvons opter pour des méthodes de synchronisation

nous pouvons faire la méthode de service entière comme synchronisée en utilisant synchronisé

mot-clé devant la méthode

Exemple::

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

ou nous pouvons mettre le bloc du code dans le bloc synchronisé

Exemple::

Synchronized(Object)

{

----Instructions-----

}

Je pense que le bloc synchronisé est meilleur que de faire toute la méthode

Synchronisé

Ved Prakash
la source