Contenedores de servlets integrados en Spring Boot
La popularidad de las aplicaciones nativas en la nube y microservicios genera un mayor uso de contenedores de servlets integrados. Spring Boot ofrece una variedad de contenedores muy fáciles de utilizar. En este POST veremos y compararemos los siguientes: Tomcat, Undertow y Jetty.
En esta comparación se medirán distintos parámetros:
Tiempos de arranque.
El rendimiento y tiempos en distintas prueba.
Uso de la CPU.
Finalmente se darán las conclusiones sacadas
Hardware utilizado
Pc: HP EliteBook 640 G3
SO: Windows 10
CPU: i5-6300U
RAM: 16GB
Disco Duro: 500GB(SSD M.2 NVME) & 500GB(HDD)
Versión Java: 1.8.0_211
Software utilizado
Rendimiento: Jmeter (Para mas información consultar documentación oficial)
Uso de la CPU: VisualVM (Para más información consultar la documentación oficial)
Tiempos de arranque
En primer lugar en el análisis vamos a comparar los tiempos de arranque con los distintos servidores, para ello vamos a arrancar nuestro servicio 5 veces con cada uno de los servidores y sacaremos el tiempo medio de inicialización para cada uno:
Server | Initialization (s) | Application Started (s) |
Tomcat | 5,83s | 19.4 |
Jetty | 4,56s | 19.64 |
Undertow | 4,78s | 20,10 |
Podemos ver como los tiempos son muy parecidos entre ellos, siendo ligeramente más rápido de media el Tomcat y algo mas lento Undertow, pero en tiempos tan parecidos no vemos ninguna ventaja en la elección de uno u otro.
Pruebas realizadas
Para poder sacar conclusiones de cada uno de los servlets, se ha diseñado un servicio que simula distintas circunstancias que podemos encontrarnos en un entorno real en nuestro servicios, y así ver como reacciona cada uno de los servlets en cada una de ellas, a continuación se detalla brevemente en que consiste cada una de estas pruebas simuladas:
Envío Json
Con esta prueba buscamos simular el comportamiento de los servelts ante una API, exactamente ante un GET, ya que lo que estaremos haciendo en solicitar a nuestro servicio información y este nos la devolverá con formato JSON. Además para ver como reaccionan ante distintos tamaños, se han preparado 3 tipos de json, el pequeño(5KB), el mediano(50KB) y el grande(250KB).
Recepción Json
Al igual que en la prueba anterior buscamos simular el comportamiento de una API, sin embargo esta vez ante un POST, en el que es el cliente el que envía la información al servicio. Esta información igual que en el anterior ha sido enviada en formato JSON y con los mismos 3 tamaños que en la prueba anterior.
Envío PDF
En esta prueba volvemos a simular una petición de tipo GET en la que el cliente solicita información al servidor, pero en este caso el lugar de devolver un JSON, se devuelve un PDF. Con esta prueba se busca ver como actúa cada servlet en el envío de fichero binarios. De nuevo se han utilizado 3 tamaños distintos: el pequeño(3KB), el mediano(200KB), y el grande(20MB). Para el grande por su gran tamaño se ha reducido el numero de iteraciones.
Recepción PDF
Muy similar a la prueba anterior pero simulando una petición de tipo POST, en la que es el cliente el que envía el fichero binario al servidor. De igual manera se han utilizado los 3 tamaños de archivo.
Operaciones Alta carga de procesador
En esta prueba se ha simulado que para procesar la petición el servicio requería de una alta carga de cpu, exactamente un calculo de los 10000 primeros números primos. Con esto se puede ver como se comporta cada uno principalmente en la gestión de hilos, aunque veremos que los resultados son muy similares entre los 3
Operaciones con delay
En esta prueba se busca medir exactamente lo mismo que en la anterior, pero en lugar de que las peticiones tarden un tiempo en procesarse por un proceso, se mantiene la petición a la espera de forma manual, de forma que todas las peticiones tarden exactamente lo mismo en ser procesadas.
Pico de peticiones
En esta prueba se busca ver como reacción cada uno de los servlets ante picos de peticiones, es decir muchas peticiones en un corto periodo de tiempo.
Carga progresiva
Esta prueba es justo lo contrario a la anterior, en lugar de medir picos de peticiones medimos mucha carga pero a lo largo de tiempo.
Resumen
A continuación podemos ver una tabla resumen de los resultados, en la que hemos añadido los rendimientos(peticiones/segundo) de las pruebas de envío y descarga de archivos: (más es mejor)
Server | JSON(5KB) | JSON(50KB) | JSON(250KB) | PDF(3KB) | PDF(200KB) | PDF(20MB) | ||||||
Envío | Recepción | Envío | Recepción | Envío | Recepción | Envío | Recepción | Envío | Recepción | Envío | Recepción | |
Tomcat | 518 | 502 | 513 | 580 | 276 | 333 | 169 | 580 | 145 | 411 | 10 | 16 |
Jetty | 510 | 489 | 446 | 396 | 72 | 375 | 518 | 360 | 184 | 302 | 4 | 3 |
Undertow | 492 | 520 | 603 | 566 | 274 | 351 | 155 | 684 | 123 | 448 | 9 | 31 |
Por otro lado tenemos los rendimientos del resto de pruebas: (más es mejor)
Server | Operaciones | Pico de peticiones | Carga progresiva | |
alta carga de CPU | retraso | |||
Tomcat | 2 | 50 | 469 | 361 |
Jetty | 2 | 50 | 459 | 400 |
Undertow | 2 | 25 | 529 | 430 |
La información completa de las pruebas se puede encontrar en el siguiente Excel:
Conclusiones
A continuación, se dará una serie de indicaciones obtenidas a través de las pruebas de rendimiento realizadas a cada servlet, en las que se podrán ver sus puntos débiles y fuertes, y por tanto deducir cuales serían los mejores casos de uso para cada uno de ellos.
Jetty
Ha demostrado un rendimiento peor que la competencia a la hora de hacer peticiones para recuperar datos (ya sean ficheros u objetos JSON) almacenados en el servicio. En cuanto al guardado de datos, especialmente de ficheros, Jetty destaca sobre todo si los ficheros no son de un tamaño muy elevado (<1MB). Respecto a pruebas de alta ocupación del procesador y pruebas con operaciones con delay con el procesador ocioso, Jetty está a la par que Tomcat con un buen resultado. También ha obtenido un resultado aceptable en pruebas donde se alcanzan picos de peticiones.
Como resultado podemos determinar que sería conveniente usar Jetty para ocasiones en las que el servicio realice muchas operaciones donde recibe peticiones del cliente con datos a almacenar o procesar, especialmente si estos datos no son muy voluminosos.
Undertow
Ha demostrado un rendimiento superior a la competencia a la hora de hacer peticiones para recuperar datos (ya sean ficheros u objetos JSON) almacenados en el servicio. En cuanto al guardado de datos, Undertow puede salir rentable si los ficheros son de un tamaño elevado (>1MB), aunque también tiene buen rendimiento recibiendo objetos JSON de cualquier tamaño. Respecto a pruebas de alta ocupación del procesador y pruebas con operaciones con delay con el procesador ocioso, el rendimiento es inferior, siendo superado por Tomcat y Jetty. A la hora de hacer pruebas donde se alcanzan picos de peticiones, Undertow también ha superado a toda su competencia.
Como resultado podemos determinar que sería conveniente usar Undertow para ocasiones en las que el cliente haga muchas peticiones para obtener datos almacenados previamente en el servicio. También destaca cuando se realizan operaciones de cualquier tipo con archivos de gran tamaño. Además, es el que mejor se desenvuelve en las pruebas donde el tráfico de peticiones es elevado.
Tomcat
Entre los tres servlets, ha demostrado ser el más equilibrado. Se maneja bien en la mayoría de las situaciones sin mostrar grandes resultados en ninguna de ellas. Obtiene buenos resultados a la hora de hacer peticiones para recuperar datos (ya sean ficheros u objetos JSON) almacenados en el servicio, pero no tan buenos como Undertow. Se desenvuelve mejor que Jetty cuando es necesario utilizar un volumen de datos grande. Respecto a pruebas de alta ocupación del procesador y pruebas con operaciones con delay con el procesador ocioso, Tomcat obtenía unos resultados similares a Jetty, siendo ambos superiores a Undertow.
Podríamos decir que el uso de Tomcat queda reservado a aquellas ocasiones en las que el servicio tiene que atender un flujo de operaciones muy variado (tanto almacenaje como recuperación de datos, incluso de gran tamaño).