Maven: Parallel builds benchmark

El uso de Maven para la gestión y construcción de los proyectos Java es, con el permiso de Graddle, prácticamente un estándar. Ahora bien, su capacidad para realizar construcciones en paralelo y la mejora de rendimiento que esto aporta puede que no sean tan conocidas.

En el presente artículo se pretende analizar cómo influye el uso de las construcciones en paralelo con diferentes configures sobre un proyecto multimodulo Java, con el objetivo determinar si su uso es o no beneficioso.

Caso de uso

Se dispone de proyecto Java construido con Maven con el siguiente árbol de dependencias.

Parallel builds

Maven 3.x dispone de una funcionalidad experimental que permite realizar construcciones en paralelo en función del número de hilos especificados.

mvn -T 4 clean install # Builds with 4 threads
mvn -T 1C clean install # 1 thread per cpu core
mvn -T 1.5C clean install # 1.5 thread per cpu core

Para ello, Maven analiza el grafo de dependencias del proyecto y paraleliza la construcción de aquellos módulos que así lo permitan, pudiendo obtener, según la documentación oficial, una mejora de rendimiento entre el 20-50%.

Para el caso de uso presentado y en función del número de hilos disponibles, se podría llegar a paralizar la construcción del primer y tercer módulo del segundo nivel, con los del tercero.

Benchmark

Batería de pruebas

Se han realizado 100 construcciones (mvn package) del proyecto con 1, 2, 4, 8 y 16 hilos, habiendo descargado previamente las librerías de terceros, con el objetivo de no introducir en los resultados problemas derivados de la red.

Por supuesto, antes de cada ejecución se ha realizado una limpieza del workspace (mvn clean) para garantizar la homogeneidad de las pruebas.

Se ha configurado también el maven-surefire-report-plugin para ejecutar los teses unitarios en paralelo, concretamente, cada clase se ejecutara en un hilo independiente.

<plugin&gt;
	<groupId&gt;org.apache.maven.plugins</groupId&gt;
	<artifactId&gt;maven-surefire-report-plugin</artifactId&gt;
	<version&gt;${maven-surefire-plugin.version}</version&gt;
	<configuration&gt;
		<parallel&gt;classes</parallel&gt;
	</configuration&gt;
</plugin&gt;

Finalmente para la medición de los tiempo se ha hecho de uso del plugin maven-profiler, el cual almacena en un fichero json el tiempo de ejecución de cada fase del ciclo de vida de Maven.

Entorno de ejecución

Las pruebas se han realizado sobre una instancia EC2 de Amazon, concretamente sobre una t2.xlarge, la cual cuenta con 4 cores de hasta 3GHz y 16GB de RAM.

Como sistema operativo se ha empleado un Red Hat Enterprise Linux 7.6 y la versión de Maven utilizada ha sido la 3.5.2 junto a OpenJDK 1.8.0-201 x64.

Resultados

Este es el tiempo total de ejecución para cada una de las configuraciones.

Como se puede observar, el tiempo baja considerablemente al pasar de uno a varios hilos, con una mejora cercana al 20%. Eso sí, la ganancia no es lineal y el hecho de añadir más hilos no es siempre beneficioso, como se puede observar en el salto de 8 a 16 hilos.

En lo que al tiempo medio de cada ejecución se refiere, los números muestran reafirman lo comentado anteriormente, logrando una mejora en todos los apartados.

Es interesante observar también la estabilidad de las ejecuciones bajo configuraciones multihilo, algo que sin duda aporta tranquilidad de cara a su uso.

Conclusiones

En conclusión, realizar construcciones en paralelo puede ser muy beneficioso dependiendo de la tipología del proyecto y del hardware empleado, por lo que es conveniente dedicar tiempo a analizar cada caso.

Ahora bien, antes de lanzarse al vació es recomendable leer con atención la documentación de Maven, ya que no todos los plugins y librerías son Thread safe y pueden acarrear errores.

Advertisements

CI: Jenkins & Bitbucket

A la hora de automatizar la ejecución de los jobs de Jenkins encargados de gestionar la integración continua de las aplicaciones, se disponen de multitud de opciones y en el presente articulo se pretende detallar como utilizar los WebHooks de Bitbucket para ello.

Caso de uso

Se dispone en Jenkins, de un job  de tipo “Pipeline“, encargado de desplegar distintos microservicios al realizar un push sobre el repositorio de Bitbucket correspondiente. La lógica del pipeline está contenida en un JenkinsFile y almacenada también en Bitbucket.

Build Token Root Plugin

Una manera de conectar Bitbucket y Jenkins es hacer uso de las ejecuciones remotas de este último. Dado que para ello es necesario disponer de un token de autenticación y desde BitBucket no es posible obtenerlo, la solución pasa hacer uso del plugin Build Token Root Plugin de Jenkins. Este plugin permite invocar jobs de manera remota, vía HTTP, sin necesidad de autenticación.

Una vez instalado, es hora de habilitar en el job de Jenkins la opción de “Lanzar ejecuciones remotas (ejem: desde ‘scripts’)“. Para ello, se debe establecer un identificador de seguridad, con el fin evitar que cualquiera pueda invocarlo. Una posibilidad es utilizar un UUID, que puede generarse online en uuidgenerator.net.

Finalmente es el momento de configurar el repositorio de BitBucket que alberga el código fuente del microservicio para lanzar un Post WebHook con cada commit.

La URL de Jenkins a introducir será la misma que una ejecución remota convencional, añadiendo el prefijo “buildByToken” a la URL, con el fin de evitar la necesidad de introducir un token. (El plugin soporta también la opción de pasar parametros al job)

A modo de ejemplo, se ha configurado el repositorio para no lanzar un WebHook con cada commit a las ramas “master” y “feature“.

Conclusiones

En conclusión, con el Build Token Root Plugin es posible lanzar ejecuciones remotas de jobs de Jenkins sin necesidad de un token de autorización.

Esto a su vez, a diferencia de otras soluciones como el plugin de Bitbucket, permite tener un único job de tipo “pipeline”, que se puede almacenar también en el SCM, re-utilizable para el despliegue de distintos microservicios.