¿Cómo funcionan los servlets? Instanciación, sesiones, variables compartidas y subprocesos múltiples

1144

Supongamos que tengo un servidor web que contiene numerosos servlets. Para la información que pasa entre esos servlets, estoy configurando variables de sesión e instancia.

Ahora, si 2 o más usuarios envían solicitudes a este servidor, ¿qué sucede con las variables de sesión?
¿Serán todos comunes para todos los usuarios o serán diferentes para cada usuario?
Si son diferentes, ¿cómo pudo el servidor diferenciar entre diferentes usuarios?

Una pregunta similar más, si hay nusuarios que acceden a un servlet en particular, entonces este servlet se instancia solo la primera vez que el primer usuario accedió o ¿se instancia para todos los usuarios por separado?
En otras palabras, ¿qué pasa con las variables de instancia?

Ku Jon
fuente

Respuestas:

1822

ServletContext

Cuando se inicia el contenedor de servlets (como Apache Tomcat ), desplegará y cargará todas sus aplicaciones web. Cuando se carga una aplicación web, el contenedor de servlet crea ServletContextuna vez y la mantiene en la memoria del servidor. La aplicación web de web.xmly todos incluidos web-fragment.xmlse analiza archivos, y cada uno <servlet>, <filter>y <listener>encontraron (o cada clase anotados con @WebServlet, @WebFiltery @WebListenerrespectivamente) se instancia una vez y se mantienen en la memoria del servidor también. Para cada filtro instanciado, su init()método se invoca con un nuevo FilterConfig.

Cuando a Servlettiene un valor <servlet><load-on-startup>o @WebServlet(loadOnStartup)mayor que 0, entonces su init()método también se invoca durante el inicio con un nuevo ServletConfig. Esos servlets se inicializan en el mismo orden especificado por ese valor ( 1es 1º, 2es 2º, etc.). Si se especifica el mismo valor para más de un servlet, entonces cada uno de esos servlets se carga en el mismo orden en que aparecen en el web.xml, web-fragment.xmlo @WebServletcarga de clase. En el caso de que el valor de "carga al inicio" esté ausente, el init()método se invocará siempre que la solicitud HTTP llegue a ese servlet por primera vez.

Cuando el contenedor de servlet finalice con todos los pasos de inicialización descritos anteriormente, ServletContextListener#contextInitialized()se invocará.

Al cerrarse la contenedor de servlets abajo, descarga todas las aplicaciones Web, invoca el destroy()método de todas sus servlets y filtros inicializados, y todo ServletContext, Servlet, Filtery Listenerlos casos están destrozados. Finalmente ServletContextListener#contextDestroyed()se invocará el.

HttpServletRequest y HttpServletResponse

El contenedor de servlet está conectado a un servidor web que escucha las solicitudes HTTP en un determinado número de puerto (el puerto 8080 generalmente se usa durante el desarrollo y el puerto 80 en producción). Cuando un cliente (por ejemplo, usuario con un navegador web, o mediante programaciónURLConnection ) envía una solicitud HTTP, el contenedor servlet crea nueva HttpServletRequesty HttpServletResponseobjetos y los pasa a través de cualquier definido Filteren la cadena y, eventualmente, la Servletinstancia.

En el caso de los filtros , doFilter()se invoca el método. Cuando el código del contenedor de servlet llama chain.doFilter(request, response), la solicitud y la respuesta continúan al siguiente filtro, o presionan el servlet si no hay filtros restantes.

En el caso de los servlets , service()se invoca el método. Por defecto, este método determina cuál de los doXxx()métodos invocar en función de request.getMethod(). Si el método determinado está ausente del servlet, se devuelve un error HTTP 405 en la respuesta.

El objeto de solicitud proporciona acceso a toda la información sobre la solicitud HTTP, como su URL, encabezados, cadena de consulta y cuerpo. El objeto de respuesta proporciona la capacidad de controlar y enviar la respuesta HTTP de la manera que desee, por ejemplo, permitiéndole configurar los encabezados y el cuerpo (generalmente con contenido HTML generado a partir de un archivo JSP). Cuando la respuesta HTTP se confirma y finaliza, los objetos de solicitud y respuesta se reciclan y se ponen a disposición para su reutilización.

HttpSession

Cuando un cliente visita la aplicación web por primera vez y / o HttpSessionse obtiene por primera vez a través de request.getSession(), el contenedor de servlet crea un nuevo HttpSessionobjeto, genera un ID largo y único (que puede obtener session.getId()) y lo almacena en el servidor memoria. El contenedor de servlets también establece una Cookieen la Set-Cookiecabecera de la respuesta HTTP con JSESSIONIDcomo su nombre y el identificador de sesión único como su valor.

Según la especificación de cookies HTTP (un contrato al que debe adherirse cualquier navegador web y servidor web decente), el cliente (el navegador web) debe enviar esta cookie en solicitudes posteriores en el Cookieencabezado mientras la cookie sea válida ( es decir, la ID única debe referirse a una sesión no vencida y el dominio y la ruta son correctos). Usando el monitor de tráfico HTTP incorporado de su navegador, puede verificar que la cookie sea válida (presione F12 en Chrome / Firefox 23+ / IE9 +, y verifique la pestaña Red / Red ). El contenedor del servlet verificará el Cookieencabezado de cada solicitud HTTP entrante en busca de la presencia de la cookie con el nombre JSESSIONIDy usará su valor (el ID de sesión) para obtener el asociado HttpSessionde la memoria del servidor.

El HttpSessionpermanece vivo hasta que haya estado inactivo (es decir, no utilizado en una solicitud) durante más tiempo que el valor de tiempo de espera especificado en <session-timeout>una configuración web.xml. El valor de tiempo de espera predeterminado es 30 minutos. Por lo tanto, cuando el cliente no visita la aplicación web durante más tiempo que el especificado, el contenedor de servlets destruye la sesión. Cada solicitud posterior, incluso con la cookie especificada, ya no tendrá acceso a la misma sesión; el contenedor de servlet creará una nueva sesión.

En el lado del cliente, la cookie de sesión permanece viva mientras se ejecuta la instancia del navegador. Entonces, si el cliente cierra la instancia del navegador (todas las pestañas / ventanas), la sesión se descarta del lado del cliente. En una nueva instancia del navegador, la cookie asociada con la sesión no existiría, por lo que ya no se enviaría. Esto hace HttpSessionque se cree una completamente nueva, con una cookie de sesión completamente nueva que se utiliza.

En una palabra

  • La ServletContextvida dura tanto como la aplicación web. Se comparte entre todas las solicitudes en todas las sesiones.
  • La HttpSessionvida dura mientras el cliente interactúa con la aplicación web con la misma instancia del navegador, y la sesión no ha expirado en el lado del servidor. Se comparte entre todas las solicitudes en la misma sesión.
  • El HttpServletRequesty HttpServletResponsevive desde el momento en que el servlet recibe una solicitud HTTP del cliente, hasta que llega la respuesta completa (la página web). Se no compartida en otro lugar.
  • Todo Servlet, Filtery Listenercasos viven tanto como vive la aplicación web. Se comparten entre todas las solicitudes en todas las sesiones.
  • Cualquiera attributeque esté definido en ServletContext, HttpServletRequesty HttpSessionvivirá mientras viva el objeto en cuestión. El objeto en sí mismo representa el "alcance" en los marcos de gestión de beans como JSF, CDI, Spring, etc. Esos marcos almacenan sus beans de ámbito como uno attributede sus ámbitos de coincidencia más cercanos.

Hilo de seguridad

Dicho esto, su mayor preocupación es posiblemente la seguridad del hilo . Ahora debe saber que los servlets y los filtros se comparten entre todas las solicitudes. Eso es lo bueno de Java, es multiproceso y diferentes subprocesos (léase: solicitudes HTTP) pueden hacer uso de la misma instancia. De lo contrario, sería demasiado costoso recrearlos init()y destroy()cada solicitud individual.

También debe darse cuenta de que nunca debe asignar ninguna solicitud o datos con alcance de sesión como una variable de instancia de un servlet o filtro. Se compartirá entre todas las demás solicitudes en otras sesiones. ¡Eso no es seguro para subprocesos! El siguiente ejemplo ilustra esto:

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

Ver también:

BalusC
fuente
25
Entonces, cuando de alguna manera puedo encontrar el JSessionId que se envía a un cliente, ¿puedo robar su sesión?
Toskan
54
@Toskan: eso es correcto. Se conoce como hack de fijación de sesión . Tenga en cuenta que esto no es específico de JSP / Servlet. Todos los demás lenguajes del lado del servidor que mantienen la sesión mediante una cookie también son sensibles, como PHP con PHPSESSIDcookie, ASP.NET con ASP.NET_SessionIDcookie, etc. Esa es también la razón por la que la reescritura de URL con ;jsessionid=xxxalgunos marcos JSP / Servlet MVC automáticamente está mal vista. Solo asegúrese de que la ID de la sesión nunca se exponga en la URL o por otros medios en las páginas web para que el usuario final no sea atacado.
BalusC
11
@Toskan: Además, asegúrese de que su aplicación web no sea sensible a los ataques XSS. Es decir, no volver a mostrar ninguna entrada controlada por el usuario en forma sin escape. XSS abrió las puertas a formas de recopilar ID de sesión de todos los usuarios finales. Vea también ¿Cuál es el concepto general detrás de XSS?
BalusC
2
@ BalusC, perdón por mi estupidez. Significa que todos los usuarios acceden a la misma instancia de thisIsNOTThreadSafe ¿verdad?
eclipsar el
44
@TwoThumbSticks 404 se devuelve cuando el servlet completo está ausente. 405 se devuelve cuando está presente el servlet pero no se implementa el método doXxx () deseado.
BalusC
428

Sesiones

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

En resumen: el servidor web emite un identificador único para cada visitante en su primera visita. El visitante debe traer esa identificación para que sea reconocido la próxima vez. Este identificador también permite al servidor segregar adecuadamente los objetos que posee una sesión contra la de otra.

Instanciación de servlet

Si load-on-startup es falso :

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Si load-on-startup es verdadero :

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Una vez que esté en el modo de servicio y en el ritmo, el mismo servlet funcionará en las solicitudes de todos los demás clientes.

ingrese la descripción de la imagen aquí

¿Por qué no es una buena idea tener una instancia por cliente? Piensa en esto: ¿Contratarás un pizzero por cada pedido que llegue? Hazlo y estarás fuera del negocio en poco tiempo.

Sin embargo, conlleva un pequeño riesgo. Recuerde: este chico solo tiene toda la información de la orden en su bolsillo: por lo tanto, si no tiene cuidado con la seguridad del hilo en los servlets , puede terminar dando la orden incorrecta a un determinado cliente.

Saltos
fuente
26
Tu foto es muy buena para mi comprensión. Tengo una pregunta: ¿qué hará este restaurante de pizza cuando lleguen demasiados pedidos de pizza, solo espere a un pizzero o contrate a más pizzeros? Gracias .
zh18
66
Él devolverá un mensaje conto many requests at this moment. try again later
Please_Dont_Bully_Me_SO_Lords
3
Los servlets, a diferencia de los repartidores de Pizza, pueden hacer más de una entrega al mismo tiempo. Sólo tienen que tener especial cuidado en donde se anoten la dirección del cliente, el sabor de la pizza ...
Bruno
42

La sesión en servlets de Java es lo mismo que la sesión en otros lenguajes como PHP. Es exclusivo del usuario. El servidor puede seguirlo de diferentes maneras, como cookies, reescritura de URL, etc. Este artículo de documentación de Java lo explica en el contexto de los servlets de Java e indica que exactamente cómo se mantiene la sesión es un detalle de implementación que se deja a los diseñadores del servidor. La especificación solo estipula que debe mantenerse como único para un usuario a través de múltiples conexiones al servidor. Consulte este artículo de Oracle para obtener más información sobre ambas preguntas.

Editar Hay un excelente tutorial aquí sobre cómo trabajar con la sesión dentro de los servlets. Y aquí hay un capítulo de Sun sobre Java Servlets, qué son y cómo usarlos. Entre esos dos artículos, debería poder responder a todas sus preguntas.

Chris Thompson
fuente
Esto me plantea otra pregunta, ya que solo hay un contexto de servlet para toda la aplicación y tenemos acceso a las variables de sesión a través de este contexto de servlet, entonces, ¿cómo pueden las variables de sesión ser únicas para cada usuario? Gracias ..
Ku Jon
1
¿Cómo accede a la sesión desde el servletContext? No te estás refiriendo a servletContext.setAttribute (), ¿verdad?
mate b
44
@KuJon Cada aplicación web tiene un ServletContextobjeto. Ese objeto tiene cero, uno o más objetos de sesión: una colección de objetos de sesión. Cada sesión se identifica mediante algún tipo de cadena de identificación, como se ve en los dibujos animados en otra respuesta. Ese identificador se rastrea en el cliente mediante cookies o reescritura de URL. Cada objeto de sesión tiene sus propias variables.
Basil Bourque
33

Cuando se inicia el contenedor de servlets (como Apache Tomcat), se leerá del archivo web.xml (solo uno por aplicación) si algo sale mal o muestra un error en la consola del lado del contenedor; de lo contrario, se desplegará y cargará toda la web aplicaciones utilizando web.xml (denominado así como descriptor de implementación).

Durante la fase de creación de instancias del servlet, la instancia de servlet está lista pero no puede atender la solicitud del cliente porque falta dos datos:
1: información de contexto
2: información de configuración inicial

El motor de servlet crea el objeto de interfaz servletConfig que encapsula la información que falta arriba en él. El motor de servlet llama init () del servlet al proporcionar referencias de objeto servletConfig como argumento. Una vez que init () se ejecuta por completo, el servlet está listo para atender la solicitud del cliente.

P) En la vida útil del servlet, ¿cuántas veces ocurre la instanciación y la inicialización?

A) solo una vez (por cada solicitud de cliente se crea un nuevo subproceso) solo una instancia del servlet sirve cualquier número de solicitud del cliente, es decir, después de servir un servidor de solicitud de cliente no muere. Espera las solicitudes de otros clientes, es decir, qué limitación de CGI (para cada solicitud de cliente que se crea un nuevo proceso) se supera con el servlet (internamente el motor de servlet crea el hilo).

P) ¿Cómo funciona el concepto de sesión?

A) cada vez que se llama a getSession () en el objeto HttpServletRequest

Paso 1 : el objeto de solicitud se evalúa para la ID de sesión entrante.

Paso 2 : si la ID no está disponible, se crea un nuevo objeto HttpSession y se genera su ID de sesión correspondiente (es decir, de HashTable), la ID de sesión se almacena en el objeto de respuesta httpservlet y la referencia del objeto HttpSession se devuelve al servlet (doGet / doPost) .

Paso 3 : si la ID disponible no se crea el nuevo objeto de sesión, la ID de sesión se recoge de la solicitud de búsqueda de objetos que se realiza en la colección de sesiones utilizando la ID de sesión como clave.

Una vez que la búsqueda es exitosa, la ID de sesión se almacena en HttpServletResponse y las referencias de objetos de sesión existentes se devuelven a doGet () o doPost () de UserDefineservlet.

Nota:

1) cuando el control sale del código de servlet al cliente, no olvide que el objeto de sesión está retenido por el contenedor de servlet, es decir, el motor de servlet

2) el subprocesamiento múltiple se deja a la gente de desarrolladores de servlets para implementar, es decir, manejar las múltiples solicitudes del cliente, nada de lo que preocuparse por el código multiproceso

Forma corta:

Se crea un servlet cuando se inicia la aplicación (se implementa en el contenedor del servlet) o cuando se accede por primera vez (según la configuración de carga al inicio) cuando se instancia el servlet, se llama al método init () del servlet entonces el servlet (su única instancia) maneja todas las solicitudes (su método service () es llamado por múltiples hilos). Es por eso que no es aconsejable tener ninguna sincronización, y debe evitar las variables de instancia del servlet cuando la aplicación no se implementa (el contenedor del servlet se detiene), se llama al método destroy ().

Ajay Takur
fuente
20

Sesiones : lo que dijo Chris Thompson.

Instanciación : se crea una instancia de un servlet cuando el contenedor recibe la primera solicitud asignada al servlet (a menos que el servlet esté configurado para cargarse en el inicio con el <load-on-startup>elemento en web.xml). La misma instancia se utiliza para atender solicitudes posteriores.

Lauri Lehtinen
fuente
3
Correcto. Pensamiento adicional: cada solicitud obtiene un hilo nuevo (o reciclado) para ejecutarse en esa única instancia de Servlet. Cada Servlet tiene una instancia y posiblemente muchos hilos (si hay muchas solicitudes simultáneas).
Basil Bourque
13

La Especificación de Servlet JSR-315 define claramente el comportamiento del contenedor web en los métodos de servicio (y doGet, doPost, doPut, etc.) (2.3.3.1 Problemas de subprocesamiento múltiple, Página 9):

Un contenedor de servlet puede enviar solicitudes concurrentes a través del método de servicio del servlet. Para manejar las solicitudes, el Desarrollador de Servlet debe tomar las medidas adecuadas para el procesamiento concurrente con múltiples hilos en el método de servicio.

Aunque no se recomienda, una alternativa para el Desarrollador es implementar la interfaz SingleThreadModel que requiere que el contenedor garantice que solo haya un hilo de solicitud a la vez en el método de servicio. Un contenedor de servlet puede satisfacer este requisito serializando solicitudes en un servlet o manteniendo un grupo de instancias de servlet. Si el servlet es parte de una aplicación web que se ha marcado como distribuible, el contenedor puede mantener un conjunto de instancias de servlet en cada JVM en la que se distribuye la aplicación.

Para los servlets que no implementan la interfaz SingleThreadModel, si el método de servicio (o métodos como doGet o doPost que se envían al método de servicio de la clase abstracta HttpServlet) se ha definido con la palabra clave sincronizada, el contenedor de servlet no puede usar el enfoque del grupo de instancias , pero debe serializar las solicitudes a través de él. Se recomienda encarecidamente que los desarrolladores no sincronicen el método de servicio (o los métodos que se le envían) en estas circunstancias debido a los efectos perjudiciales en el rendimiento

tharindu_DG
fuente
2
Para su información, la especificación actual de Servlet (2015-01) es 3.1, definida por JSR 340 .
Basil Bourque
1
Muy buena respuesta! @tharindu_DG
Tom Taylor
0

Como queda claro en las explicaciones anteriores, al implementar SingleThreadModel , el servlet puede garantizar la seguridad de la rosca mediante el contenedor de servlets. La implementación del contenedor puede hacer esto de 2 maneras:

1) Solicitudes de serialización (colas) a una sola instancia: esto es similar a un servlet que NO implementa SingleThreadModel PERO sincronizando los métodos de servicio / doXXX; O

2) Crear un grupo de instancias, que es una mejor opción y una compensación entre el esfuerzo de arranque / inicialización / tiempo del servlet frente a los parámetros restrictivos (tiempo de memoria / CPU) del entorno que aloja el servlet.

Mahesh Balasubramanian
fuente
-1

No. Los servlets no son seguros para subprocesos

Esto permite acceder a más de un hilo a la vez

si quieres que sea Servlet como Thread safe., U puedes elegir

Implement SingleThreadInterface(i) que es una interfaz en blanco no hay

métodos

o podemos ir por métodos de sincronización

podemos hacer que todo el método de servicio esté sincronizado mediante el uso sincronizado

palabra clave delante del método

Ejemplo::

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

o podemos poner el bloque put del código en el bloque Sincronizado

Ejemplo::

Synchronized(Object)

{

----Instructions-----

}

Siento que el bloqueo sincronizado es mejor que hacer todo el método

Sincronizado

Ved Prakash
fuente