web-dev-qa-db-fra.com

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

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

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? Si elles sont différentes, comment le serveur a-t-il pu différencier les différents utilisateurs?

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

1041
Ku Jon

ServletContext

Lorsque le conteneur de servlet (tel que Apache Tomcat ) démarre, il déploie et charge toutes ses applications Web. Lorsqu'une application Web est chargée, le conteneur de servlets crée ServletContext une fois et le conserve dans la mémoire du serveur. Le web.xml de l'application Web _ et tous les fichiers web-fragment.xml inclus sont analysés, et chaque <servlet>, <filter> et <listener> trouvés (ou chaque classe annotée avec @WebServlet, @WebFilter et @WebListener) sont instanciés une fois et conservés également dans la mémoire du serveur. Pour chaque filtre instancié, sa méthode init() est appelée avec un nouveau FilterConfig .

Quand un Servlet a un <servlet><load-on-startup> ou @WebServlet(loadOnStartup) supérieur à 0, sa méthode init() est également appelée au démarrage avec un nouveau ServletConfig . Ces servlets sont initialisés dans le même ordre que celui spécifié par cette valeur (1 est 1er, 2 est 2e, etc.). Si la même valeur est spécifiée pour plusieurs servlets, chacun de ces servlets est chargé dans le même ordre d'apparition dans le chargement de classe web.xml, web-fragment.xml ou @WebServlet. Si la valeur "load-on-startup" est absente, la méthode init() sera invoquée chaque fois que la demande HTTP rencontre ce servlet pour la première fois.

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

Lorsque le conteneur de servlets s'arrête, il décharge toutes les applications Web, appelle la méthode destroy() de tous ses servlets et filtres initialisés et toutes les instances ServletContext, Servlet, Filter et Listener sont supprimées. Enfin, le ServletContextListener#contextDestroyed() sera appelé.

HttpServletRequest et HttpServletResponse

Le conteneur de servlets est connecté à un serveur Web qui écoute les demandes 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 (par exemple, un utilisateur disposant d'un navigateur Web ou en utilisant URLConnection ) par programme envoie une requête HTTP, le conteneur de servlet crée un nouveau HttpServletRequest et HttpServletResponse objets et les transmet à travers tout Filter défini dans la chaîne et, éventuellement, l'instance Servlet.

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

Dans le cas de servlets , la méthode service() est invoquée. Par défaut, cette méthode détermine la méthode à appeler doXxx() à utiliser à partir de 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 requête fournit un accès à toutes les informations sur la requête 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 le contenu HTML généré à partir d'un fichier JSP). Lorsque la réponse HTTP est validée et terminée, les objets requête et réponse sont tous deux recyclés et rendus disponibles pour une réutilisation.

HttpSession

Lorsqu'un client visite l'application Web pour la première fois et/ou que HttpSession est obtenu pour la première fois via request.getSession(), le conteneur de servlet crée un nouvel objet HttpSession et génère un ID long et unique ( que vous pouvez obtenir par session.getId()) et l’enregistre dans la mémoire du serveur. Le conteneur de servlet définit également une Cookie dans l'en-tête Set-Cookie de la réponse HTTP avec JSESSIONID comme nom et l'ID de session unique comme valeur.

Conformément à la spécification de cookie HTTP (un contrat que tout navigateur Web et serveur Web doivent respecter), le client (le navigateur Web) est tenu de renvoyer ce cookie lors de demandes ultérieures dans le répertoire. L'entête Cookie aussi longtemps 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). À l'aide du moniteur de trafic HTTP intégré à votre navigateur, vous pouvez vérifier que le cookie est valide (appuyez sur la touche F12 dans Chrome/Firefox 23+/IE9 +, puis cochez la case Net/Network onglet). Le conteneur de servlet vérifiera l'en-tête Cookie de chaque demande HTTP entrante pour détecter la présence du cookie portant le nom JSESSIONID et utilisera sa valeur (l'ID de session) pour extraire le HttpSession associé de la mémoire du serveur.

HttpSession reste en vie jusqu'à ce qu'il soit inactif (c'est-à-dire non utilisé dans une demande) plus que la valeur de délai d'attente spécifiée dans <session-timeout>, paramètre défini dans web.xml. La valeur de délai d'attente par défaut est de 30 minutes. Ainsi, lorsque le client ne visite pas l'application Web plus longtemps que l'heure spécifiée, le conteneur de servlets ferme la session. Toute demande ultérieure, même avec le cookie spécifié, n'aura plus accès à la même session; le conteneur de servlet va créer une nouvelle session.

Du côté du client, le cookie de session reste actif aussi longtemps que l'instance du navigateur est en cours d'exécution. Ainsi, si le client ferme l'instance du navigateur (tous les onglets/toutes les fenêtres), la session est alors supprimée du côté du client. Dans une nouvelle instance de navigateur, le cookie associé à la session n'existerait pas et ne serait donc plus envoyé. Cela entraîne la création d'une HttpSession entièrement nouvelle, avec utilisation d'un nouveau cookie de session.

En un mot

  • ServletContext vit aussi longtemps que l’application Web existe. Il est partagé entre all requêtes dans les all sessions.
  • HttpSession vit 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 les all requêtes dans la même session.
  • Les variables HttpServletRequest et HttpServletResponse sont actives à partir du moment où le servlet reçoit une demande HTTP du client jusqu'à la réponse complète (la page Web) est arrivée. C'est not partagé ailleurs.
  • Toutes les instances Servlet, Filter et Listener sont conservées aussi longtemps que l'application Web est en vie. Elles sont partagées entre all requêtes dans les all sessions.
  • Tout attribute défini dans ServletContext, HttpServletRequest et HttpSession vivra aussi longtemps que l'objet en question vivra. L'objet lui-même représente la "portée" dans les cadres de gestion de beans tels que JSF, CDI, Spring, etc. Ces cadres stockent leurs beans définis comme un attribute de sa portée la plus proche.

Sécurité du fil

Cela dit, votre préoccupation majeure est peut-être thread safety . 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, c’est multithread et différents threads (read: requêtes HTTP) peuvent utiliser la même instance. Il serait sinon trop coûteux de les recréer, init() et destroy() pour chaque requête.

Vous devez également vous rendre compte que vous devez ne jamais attribuer des données de demande ou de portée de session sous forme de variable instance de une servlet ou un filtre. Il sera partagé entre toutes les autres demandes dans les autres sessions. C'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:

1760
BalusC

Sessions

enter image description hereenter image description here

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

Instanciation de servlet

Si load-on-startup est false:

enter image description hereenter image description here

Si load-on-startup est true:

enter image description hereenter image description here

Une fois qu'il est en mode service et sur le groove, la servlet identique travaillera sur les demandes de tous les autres clients. 

enter image description here

Pourquoi n’est-ce pas une bonne idée d’avoir une instance par client? Pensez à ceci: Engagerez-vous un livreur de pizza pour chaque commande passée? Faites cela et vous feriez faillite en un rien de temps.

Cela vient avec un petit risque cependant. Rappelez-vous: ce célibataire a toutes les informations de la commande dans sa poche: donc, si vous n’êtes pas prudent sur la sécurité des threads sur les servlets , il risque de donner le mauvais ordre à un client donné.

413
Jops

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

Edit Il y a un excellent tutoriel ici sur la façon de travailler avec des sessions à l'intérieur de servlets. Et here est un chapitre de Sun sur les servlets Java, leur nature et leur utilisation. Entre ces deux articles, vous devriez pouvoir répondre à toutes vos questions. 

42
Chris Thompson

Lorsque le conteneur de servlets (comme Apache Tomcat) démarre, il lit le fichier web.xml (un seul par application) en cas de problème ou affiche une erreur sur la console côté conteneur. Sinon, il déploiera et chargera toutes les données Web. applications en utilisant web.xml (ainsi nommé comme descripteur de déploiement).

Au cours de la phase d'instanciation du 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 Le moteur de servlet appelle init () du servlet en fournissant des références d'objet servletConfig en tant qu'argument. Une fois que init () est complètement exécuté, le servlet est prêt à répondre à la demande du client. 

Q) Dans la durée de vie de la servlet, combien de fois l’instanciation et l’initialisation ont lieu?

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

Q) Comment fonctionne le concept de session?

A) chaque fois que getSession () est appelé sur l'objet HttpServletRequest 

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

É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 l'ID disponible n'est pas créé, le nouvel objet de session n'est pas créé L'ID de session est choisi à partir de l'objet de requête. La recherche s'effectue 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 servlets, c'est-à-dire le moteur de servlets

2) le multithreading est laissé aux développeurs de servlets pour l'implémentation, c'est-à-dire, gérer les multiples requêtes du client 

Forme Inshort:

Un servlet est créé au démarrage de l'application (il est déployé sur le conteneur de servlets) ou au premier accès (en fonction du paramètre load-on-startup) Lorsque le servlet est instancié, la méthode init () de la servlet est appelé 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 est déconseillé de procéder à une synchronisation et d'é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.

33
Ajay Takur

Sessions - ce que Chris Thompson a dit.

Instanciation - un servlet est instancié lorsque le conteneur reçoit la première demande adressée au servlet (à moins que le servlet ne soit configuré pour se charger au démarrage avec l'élément <load-on-startup> dans web.xml). La même instance est utilisée pour répondre aux demandes suivantes.

20
Lauri Lehtinen

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 servlets peut envoyer des demandes simultanées via le service méthode de la servlet. Pour traiter 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 le conteneur pour garantir qu'il n'y ait qu'un seul thread de requête à la fois dans le fichier méthode de service. Un conteneur de servlets peut satisfaire à cette exigence avant le sérialiser les demandes sur un servlet ou maintenir un pool de servlets les instances. Si le servlet fait partie d'une application Web ayant été marqué comme distribuable, le conteneur peut maintenir un pool de servlets instances de chaque machine virtuelle Java sur lesquelles l’application est distribuée.

Pour les servlets n'implémentant pas l'interface SingleThreadModel, si le fichier 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éfini 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 à travers. Il est fortement recommandé aux développeurs de ne pas synchroniser la méthode de service (ou les méthodes qui y sont envoyées) dans ces circonstances en raison d'effets néfastes sur la performance

13
tharindu_DG

Comme il ressort des explications ci-dessus, en mettant en œuvre le SingleThreadModel, une servlet peut être assurée par le conteneur de servlets. L'implémentation de conteneur peut le faire de deux manières:

1) Sérialiser les demandes (mise en file d'attente) sur une seule instance - ceci est similaire à un servlet qui n'implémente pas SingleThreadModel BUT mais qui synchronise les méthodes service/doXXX; OU

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

0