A través de este artículo, explicaremos principalmente los problemas encontrados en el procesamiento asíncrono de Servlet 3.0 en el desarrollo de Java y las soluciones. Los siguientes son los contenidos específicos:
Servlet 3.0 ha comenzado a proporcionar AsyncContext para admitir el procesamiento asincrónico de las solicitudes. Entonces, ¿qué beneficios pueden traer el procesamiento asincrónico de las solicitudes?
En términos generales, la forma en que los contenedores web manejan las solicitudes es asignar un hilo a cada solicitud. Todos sabemos que la creación de hilos no está exento de costo, y el conjunto de contenedores web de hilos tiene un límite superior.
Un problema muy predecible es que en condiciones de alta carga, el grupo de subprocesos está ocupado, por lo que la solicitud posterior solo puede esperarse. Si no tiene suerte, el cliente informará un error de tiempo de espera de espera.
Antes de que apareciera AsyncContext, la única forma de resolver este problema era expandir el grupo de hilos del contenedor web.
Pero todavía hay un problema con esto, considere los siguientes escenarios:
Hay un contenedor web con un tamaño de grupo de subprocesos de 200. Hay una aplicación web que tiene dos servlets, el tiempo que lleva Servlet-A para manejar una sola solicitud es de 10s, y el tiempo que lleva Servlet-B para manejar una sola solicitud es 1s.
Ahora encontramos una alta carga, con más de 200 solicitudes para Servlet-A. Si Servlet-B se solicita en este momento, esperaremos porque todos los hilos HTTP han sido ocupados por Servlet-A.
En este momento, el ingeniero descubrió el problema y amplió el tamaño del grupo de subprocesos a 400, pero la carga continuó aumentando. Ahora hay 400 solicitudes para Servlet-A, y Servlet-B aún no puede responder.
¿Has visto el problema? Debido a que el hilo HTTP y el hilo de los trabajadores se acoplan juntos, llenará el hilo HTTP cuando se realice una gran cantidad de solicitudes a una operación que requiere mucho tiempo, lo que resulta en que todo el contenedor web no pueda responder.
Sin embargo, si usamos AsyncContext, podemos entregar la operación que consumen mucho tiempo a otro subproceso, de modo que el hilo HTTP se libere y podamos manejar otras solicitudes.
Tenga en cuenta que solo usar AsyncContext puede lograr el efecto mencionado anteriormente. Si usa directamente los nuevos hilos () o métodos similares, el hilo HTTP no se devolverá al contenedor.
Aquí hay un ejemplo oficial:
@WebServlet (urlPatterns = {"/asyncservlet"}, asyncSupported = true) clase pública AsyncServlet extiende httpservlet {/ * ... mismas variables y método init como en sincservlet ... */@override public void doget (httPservletRequest Solicitud, httpServletRespessesspesselt) respuesta.setContentType ("text/html; charset = utf-8"); ASYNCCONTEXT final acontext = request.startasync (); acontext.start (new runnable () {public void run () {String param = acontext.getRequest (). getParameter ("param"); string dultin = resource.process (param); httpservletResponse respuesta = acontext.getResponse (); / * ... imprimir a la respuesta ... * / acontext.complete ();}}); }} trampa
En este ejemplo oficial, cada hilo HTTP abrirá otro hilo de trabajadores para procesar la solicitud y luego devolverá el hilo HTTP al contenedor web. Pero mire el Javadoc del método asynccontext.start ():
Hace que el contenedor envíe un hilo, posiblemente desde un grupo de hilos administrado, para ejecutar el runnable especificado.
De hecho, no hay regulación aquí de donde proviene el hilo del trabajador. ¿Quizás es otro grupo de hilos que no sea el grupo de hilos HTTP? ¿O es solo una piscina de hilo HTTP?
La utilidad limitada de AsyncContext.start () Artículo dice: Los diferentes contenedores web tienen diferentes implementaciones para esto, pero Tomcat en realidad usa el grupo de subprocesos HTTP para manejar asynccontext.start ().
Esto significa que originalmente queríamos liberar el hilo HTTP, pero de hecho no lo hizo, porque el hilo HTTP todavía se usa como el hilo del trabajador, pero este hilo no es lo mismo que el hilo HTTP que recibe la solicitud.
También podemos ver esta conclusión a través del punto de referencia JMeter de Asyncservlet1 y SyncServlet, y los resultados del dos rendimiento son similares. Método de inicio: Inicie el principal y luego use JMeter para iniciar Benchmark.jmx (HTTP Thread Pool = 200 bajo la configuración predeterminada de Tomcat).
Usando ExecutorService
Vi anteriormente que Tomcat no mantiene el grupo de hilos de trabajadores por separado, por lo que tenemos que encontrar una manera de hacerlo nosotros mismos, ver AsyncServlet2, que utiliza un servicio de ejecutores con grupo de subprocesos para manejar AsyncContext.
Otras formas
Por lo tanto, no hay forma fija de usar AsyncContext. Puede usar diferentes métodos para tratarlo de acuerdo con las necesidades reales. Por esta razón, necesita algún conocimiento de la programación concurrente de Java.
Malentendido del rendimiento
El propósito de AsyncContext no es mejorar el rendimiento, ni proporciona directamente la mejora del rendimiento. Proporciona un mecanismo para desacoplar el hilo HTTP y el hilo de los trabajadores, mejorando así la capacidad de respuesta de los contenedores web.
Sin embargo, AsyncContext puede mejorar el rendimiento en algún momento, pero esto depende de cómo se escriba su código.
Por ejemplo: el número de grupos de hilos HTTP en un contenedor web es de 200, y un servlet utiliza un grupo de subprocesos de 300 trabajadores para manejar AsyncContext.
En comparación con el grupo de hilo de trabajadores de Sync Method = HTTP PULS = 200, en este caso tenemos un grupo de hilo de trabajadores de 300, por lo que definitivamente traerá algunas mejoras de rendimiento (después de todo, hay más personas trabajando).
Por el contrario, si el número de hilos de trabajadores es <= el número de hilos HTTP, no habrá mejoras del rendimiento, porque el cuello de botella para las solicitudes de procesamiento está en el hilo de trabajadores.
Puede modificar el tamaño del grupo de subprocesos de AsyncServlet2 y compararlo con los resultados de referencia SyncServlet para verificar esta conclusión.
No piense que el grupo de hilos de trabajadores debe ser más grande que el grupo de hilos HTTP. Las razones son las siguientes:
Las dos responsabilidades son diferentes. Una es que el contenedor web se utiliza para recibir solicitudes externas , y el otro es procesar la lógica comercial.
Crear hilos tiene un costo. Si el grupo de subprocesos HTTP ya es grande, entonces crear un grupo de subprocesos de trabajadores más grandes causará demasiado interruptor de contexto y sobrecarga de memoria.
El propósito de AsyncContext es liberar el hilo HTTP para evitar el uso a largo plazo de las operaciones y, por lo tanto, hacer que el contenedor web no pueda responder.
Entonces, en la mayoría de los casos, el grupo de subprocesos de trabajadores no será muy grande, y se construirán diferentes grupos de hilos de trabajadores de acuerdo con diferentes empresas.
Por ejemplo: el tamaño del grupo de subprocesos del contenedor web es 200, y el tamaño del grupo de subprocesos de trabajadores es 10 para un servlet lento. De esta manera, no importa cuántas solicitudes se usen para ralentizar operaciones, no llenará el hilo HTTP y hará que otras solicitudes no puedan procesar.