Este artículo explora nuestra decisión de alejarnos de la arquitectura reactiva en nuestro proyecto de software. Profundizaremos en los principios básicos de los sistemas reactivos, los beneficios de las E/S sin bloqueo y los desafíos que enfrentamos con un enfoque reactivo.
Reactivo abarca un conjunto de principios y directrices destinados a construir sistemas y aplicaciones distribuidos responsivos, caracterizados por:
Un beneficio clave de los sistemas reactivos es el uso de E/S sin bloqueo. Este enfoque evita el bloqueo de subprocesos durante las operaciones de E/S, lo que permite que un solo subproceso maneje múltiples solicitudes al mismo tiempo. Esto puede mejorar significativamente la eficiencia del sistema en comparación con el bloqueo de E/S tradicional.
En el multiproceso tradicional, las operaciones de bloqueo plantean desafíos importantes a la hora de optimizar los sistemas (Figura 1). Las aplicaciones codiciosas que consumen demasiada memoria son ineficientes y penalizan a otras aplicaciones, y a menudo requieren solicitudes de recursos adicionales como memoria, CPU o máquinas virtuales más grandes.
Figura 1: subprocesos múltiples tradicionales
Las operaciones de E/S son parte integral de los sistemas modernos, y administrarlas de manera eficiente es fundamental para evitar comportamientos codiciosos. Los sistemas reactivos emplean E/S sin bloqueo, lo que permite que una pequeña cantidad de subprocesos del sistema operativo manejen numerosas operaciones de E/S simultáneas.
Aunque la E/S sin bloqueo ofrece beneficios sustanciales, introduce un modelo de ejecución novedoso distinto de los marcos tradicionales. La programación reactiva surgió para abordar este problema, ya que mitiga la ineficiencia de los subprocesos de la plataforma inactivos durante las operaciones de bloqueo (Figura 2).
Figura 2: bucle de evento reactivo
Quarkus aprovecha un motor reactivo impulsado por Eclipse Vert.x y Netty, lo que facilita interacciones de E/S sin bloqueo. Mutiny, el enfoque preferido para escribir código reactivo con Quarkus, adopta un paradigma basado en eventos, en el que las reacciones se desencadenan mediante eventos recibidos.
Mutiny ofrece dos tipos basados en eventos y diferidos:
Si bien los sistemas reactivos ofrecen beneficios, encontramos varios desafíos durante el desarrollo:
"De hecho, la proporción de tiempo dedicado a leer versus escribir es muy superior a 10 a 1. Leemos constantemente código antiguo como parte del esfuerzo por escribir código nuevo. ...[Por lo tanto,] facilitar la lectura facilita es más fácil escribir."
― Robert C. Martin, Código limpio: un manual de artesanía de software ágil
Aquí hay un ejemplo de código reactivo que usa Mutiny para ilustrar la complejidad:
Multi.createFrom().ticks().every(Duration.ofSeconds(15)) .onItem().invoke(() - > Multi.createFrom().iterable(configs()) .onItem().transform(configuration - > { try { return Tuple2.of(openAPIConfiguration, RestClientBuilder.newBuilder() .baseUrl(new URL(configuration.url())) .build(MyReactiveRestClient.class) .getAPIResponse()); } catch (MalformedURLException e) { log.error("Unable to create url"); } return null; }).collect().asList().toMulti().onItem().transformToMultiAndConcatenate(tuples - > { AtomicInteger callbackCount = new AtomicInteger(); return Multi.createFrom().emitter(emitter - > Multi.createFrom().iterable(tuples) .subscribe().with(tuple - > tuple.getItem2().subscribe().with(response - > { emitter.emit(callbackCount.incrementAndGet()); if (callbackCount.get() == tuples.size()) { emitter.complete(); } }) )); }).subscribe().with(s - > {}, Throwable::printStackTrace, () - > doSomethingUponComplete())) .subscribe().with(aLong - > log.info("Tic Tac with iteration: " aLong));
Project Loom, un desarrollo reciente en el ecosistema Java, promete mitigar los problemas asociados con las operaciones de bloqueo. Al permitir la creación de miles de subprocesos virtuales sin cambios de hardware, Project Loom podría eliminar potencialmente la necesidad de un enfoque reactivo en muchos casos.
"Project Loom va a acabar con la programación reactiva"
―Brian Goetz
En conclusión, nuestra decisión de alejarnos del estilo de arquitectura reactiva y adoptar un enfoque pragmático para la mantenibilidad a largo plazo de nuestro proyecto. Si bien los sistemas reactivos ofrecen beneficios potenciales, los desafíos que presentaron para nuestro equipo superaron esas ventajas en nuestro contexto específico.
Es importante destacar que este cambio no comprometió el rendimiento. Este es un resultado positivo, ya que demuestra que una arquitectura no reactiva (imperativa) bien diseñada puede ofrecer el rendimiento necesario sin la complejidad asociada con la arquitectura reactiva en nuestro caso.
Mientras miramos hacia el futuro, el enfoque sigue siendo construir una base de código que no solo sea funcional sino también fácil de entender y mantener para desarrolladores de todos los niveles de experiencia. Esto no solo reduce el tiempo de desarrollo sino que también fomenta una mejor colaboración y el intercambio de conocimientos dentro del equipo.
En el siguiente gráfico, el eje X representa la creciente complejidad de nuestra base de código a medida que evoluciona, mientras que el eje Y representa el tiempo necesario para estos cambios de desarrollo.
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3