with programs form example code java servlets asynchronous

java - programs - Evite esperar en las secuencias de Servlet



jsp example programs (3)

  1. Usted no puede El contenedor Servlet asigna el subproceso a la solicitud, y ese es el final, está asignado. Ese es el modelo. Si no te gusta eso, tendrás que dejar de usar Servlets.
  2. Incluso si pudiera resolver (1), no puede iniciar E / S asíncrona en un flujo de entrada.
  3. La forma de manejar las solicitudes lentas es cronometrarlas, estableciendo la configuración adecuada para cualquier contenedor que esté utilizando ... si realmente tiene un problema, y ​​está lejos de ser claro, con un servidor casi inactivo y esto solo sucede raramente
  4. Su bucle de lectura hace una distinción sin una diferencia. Simplemente lea la secuencia de entrada de solicitud hasta su final. El contenedor de servlets ya garantiza que el final de la secuencia ocurra en la longitud del contenido, si se proporciona.

Mi Servler dedica bastante tiempo a leer request.getInputStream() y escribir a response.getOutputStream() . A la larga, esto puede ser un problema, ya que bloquea un hilo por nada más que leer / escribir literalmente unos pocos bytes por segundo. (*)

Nunca me interesa un dato de solicitud parcial, el procesamiento no debe comenzar antes de que la solicitud esté completamente disponible. Del mismo modo para la respuesta.

Supongo que IO asíncrono lo resolvería, pero me pregunto cuál es la forma correcta. ¿Tal vez un Filter servlet que reemplace el ServletInputStream por un ByteArrayInputStream , utilizando request.startAsync y llamando al servlet encadenado después de haber recopilado toda la entrada?

  • ¿Ya hay tal filtro?
  • ¿Debo escribir uno o debo usar un enfoque diferente?

Tenga en cuenta que lo que quiero decir es evitar el desperdicio de subprocesos en flujos de servlet lentos . Esto no es lo mismo que startAsync que evita desperdiciar subprocesos solo esperando algún evento .

Y sí, en este momento sería una optimización prematura.

Mi bucle de lectura a lo solicitado

No hay nada interesante en mi actual método de lectura de flujo de entrada, pero aquí están:

private byte[] getInputBytes() throws IOException { ServletInputStream inputStream = request.getInputStream(); final int len = request.getContentLength(); if (len >= 0) { final byte[] result = new byte[len]; ByteStreams.readFully(inputStream, result); return result; } else { return ByteStreams.toByteArray(inputStream); } }

Eso es todo y se bloquea cuando los datos no están disponibles; ByteStreams provienen de guayaba.

Resumen de mi entendimiento hasta ahora

Como las respuestas lo indican claramente, es imposible trabajar con flujos de servlets sin perder un hilo en ellos. Ni la arquitectura de servlets ni la implementación común exponen nada que permita decir "almacenar en búfer los datos completos y llamarme solo cuando se recopiló todo", aunque usen NIO y puedan hacerlo.

La razón puede ser que generalmente se utiliza un proxy inverso como nginx, que puede hacerlo. nginx hace este búfer de forma predeterminada y no se puede desconectar hasta hace dos años .

En realidad un caso apoyado?

Dado que muchas respuestas negativas, no estoy seguro, pero parece que mi objetivo

para evitar perder hilos en flujos de servlet lentos

en realidad es totalmente compatible : desde 3.1, existe ServletInputStream.html#setReadListener que parece estar destinado exactamente para esto. El subproceso asignado para procesar Servlet#Service inicialmente llama a request.startAsync() , adjunta el oyente y se devuelve al grupo simplemente regresando del service . El oyente implementa onDataAvailable() , que se llama cuando es posible leer sin bloquear , agrega un dato y lo devuelve. En onAllDataRead() , puedo hacer todo el procesamiento de los datos recopilados.

Hay un example , cómo se puede hacer con Jetty. Parece cubrir también la salida no bloqueante.

(*) En los archivos de registro, puedo ver que las solicitudes tardan hasta ocho segundos y se gastan en la lectura de la entrada (encabezado de 100 bytes + datos de 100 bytes). Estos casos son raros, pero ocurren, aunque el servidor está casi inactivo. Así que supongo que es un cliente móvil con una conexión muy mala (algunos usuarios nuestros se conectan desde lugares con una conectividad tan mala).


Hay una clase llamada org.apache.catalina.connector.CoyoteAdapter , que es la clase que recibe la solicitud calculada desde el subproceso de trabajo TCP. Tiene un método llamado "servicio" que hace la mayor parte del trabajo pesado. Este método es llamado por otra clase: org.apache.coyote.http11.Http11Processor que también tiene un método del mismo nombre.

Me parece interesante ver tantos enganches en el código para manejar async io, lo que me hace preguntarme si esto ya no es una característica incorporada del contenedor. De todos modos, con mi conocimiento limitado, la mejor manera que se me ocurra para implementar la función de la que estás hablando, sería crear una clase:

public class MyAsyncReqHandlingAdapter extends CoyoteAdapter y @Override service() y le da la @Override service() su propio ... No tengo tiempo para dedicarme a hacer esto ahora, pero es posible que public class MyAsyncReqHandlingAdapter extends CoyoteAdapter @Override service() en el futuro.

En este método, necesitaría una forma de identificar las solicitudes lentas y manejarlas, entregándolas a un solo procesador de nio con hilos y "complete" la solicitud a ese nivel, que, dado el código fuente:

https://github.com/apache/tomcat/blob/075920d486ca37e0286586a9f017b4159ac63d65/java/org/apache/coyote/http11/Http11Processor.java

https://github.com/apache/tomcat/blob/3361b1321201431e65d59d168254cff4f8f8dc55/java/org/apache/catalina/connector/CoyoteAdapter.java

Usted debe ser capaz de averiguar cómo hacerlo. Pregunta interesante y sí se puede hacer. Nada de lo que veo en la especificación dice que no puede ...


HttpServletRequest#startAsync() no es útil para esto. Eso solo es útil para empujar cosas como sockets web y el buen SSE. Por otra parte, JSR356 Web Socket API está construido sobre él.

Se entiende su problema concreto, pero esto definitivamente no puede resolverse desde el servlet encendido. Solo terminaría desperdiciando aún más subprocesos por una razón muy simple porque el contenedor ya ha dedicado el subproceso actual a la solicitud del servlet hasta que el cuerpo de la solicitud se lea completamente hasta el último bit, incluso si finalmente es leído por un nuevo engendro. hilo asíncrono.

Para guardar subprocesos, realmente necesita un servletcontainer que admita NIO y, si es necesario, active esa función. Con NIO, un solo subproceso puede manejar tantas conexiones TCP como la memoria disponible del montón lo permita, en lugar de que se asigne un solo subproceso por conexión TCP. Entonces, en su servlet no necesita preocuparse por esta delicada tarea de E / S.

Casi todos los servletcontainers modernos lo soportan: Undertow (WildFly), Grizzly (GlassFish / Payara), Tomcat , Jetty , etc. Algunos lo tienen habilitado por defecto, otros requieren configuración adicional. Simplemente consulte su documentación utilizando la palabra clave "NIO".

Si en realidad también desea guardar el subproceso de solicitud del servlet, entonces básicamente debería retroceder un paso, soltar los servlets e implementar un servicio personalizado basado en NIO sobre un conector NIO existente (Undertow, Grizzly, Jetty, etc).