Spring es un marco web reactivo y sin bloqueo para crear aplicaciones web modernas y escalables en Java. Es parte de Spring Framework y utiliza la biblioteca Reactor para implementar la programación reactiva en Java. WebFlux Con WebFlux, puede crear aplicaciones web escalables y de alto rendimiento que pueden manejar una gran cantidad de solicitudes simultáneas y flujos de datos. Admite una amplia gama de casos de uso, desde API REST simples hasta transmisión de datos en tiempo real y eventos enviados por el servidor. Spring WebFlux proporciona un modelo de programación basado en flujos reactivos, que le permite componer operaciones asincrónicas y sin bloqueo en una canalización de etapas de procesamiento de datos. También proporciona un amplio conjunto de funciones y herramientas para crear aplicaciones web reactivas, incluida la compatibilidad con el acceso a datos reactivos, la seguridad reactiva y las pruebas reactivas. Del : documento oficial de Spring El término "reactivo" se refiere a los modelos de programación que se crean para reaccionar al cambio: componentes de red que reaccionan a eventos de E/S, controladores de interfaz de usuario que reaccionan a eventos del mouse y otros. En ese sentido, el no bloqueo es reactivo, porque, en lugar de estar bloqueados, ahora estamos en el modo de reaccionar a las notificaciones a medida que se completan las operaciones o los datos están disponibles. Modelo de roscado Una de las características principales de la programación reactiva es su modelo de subprocesos, que es diferente del modelo tradicional de subprocesos por solicitud que se utiliza en muchos marcos web síncronos. En el modelo tradicional, se crea un nuevo subproceso para manejar cada solicitud entrante y ese subproceso se bloquea hasta que se procesa la solicitud. Esto puede generar problemas de escalabilidad cuando se manejan grandes volúmenes de solicitudes, ya que la cantidad de subprocesos necesarios para manejar las solicitudes puede volverse muy grande y el cambio de contexto de subprocesos puede convertirse en un cuello de botella. Por el contrario, WebFlux utiliza un modelo sin bloqueo, basado en eventos, donde una pequeña cantidad de subprocesos puede manejar una gran cantidad de solicitudes. Cuando llega una solicitud, uno de los subprocesos disponibles la maneja, y luego delega el procesamiento real a un conjunto de tareas asincrónicas. Estas tareas se ejecutan sin bloqueo, lo que permite que el subproceso avance para manejar otras solicitudes mientras las tareas se ejecutan en segundo plano. En Spring WebFlux (y en los servidores sin bloqueo en general), se supone que las aplicaciones no se bloquean. Por lo tanto, los servidores sin bloqueo utilizan un pequeño grupo de subprocesos de tamaño fijo (trabajadores de bucle de eventos) para manejar las solicitudes. El modelo de subprocesamiento simplificado de un contenedor de Servlet clásico se ve así: Si bien el procesamiento de solicitudes de WebFlux es ligeramente diferente: Bajo el capó Avancemos y veamos qué hay detrás de la brillante teoría. Necesitamos una aplicación bastante minimalista generada por . El código está disponible en el . Spring Initializr repositorio de GitHub Todos los temas relacionados con subprocesos dependen mucho de la CPU. Por lo general, la cantidad de subprocesos de procesamiento que manejan las solicitudes está relacionada con la cantidad de Con fines educativos, puede manipular fácilmente el recuento de subprocesos en un grupo al limitar las CPU al ejecutar el contenedor Docker: núcleos de CPU . docker run --cpus=1 -d --rm --name webflux-threading -p 8081:8080 local/webflux-threading Si aún ve más de un subproceso en un grupo, está bien. Puede haber establecidos por WebFlux. valores predeterminados Nuestra aplicación es un simple adivino. Al llamar endpoint obtendrá 5 registros con . Cada ajuste es un número entero que representa un karma que se te ha dado. Sí, somos muy generosos porque la aplicación genera solo números positivos. ¡Ya no hay mala suerte! /karma balanceAdjustment Procesamiento por defecto Comencemos con un ejemplo muy básico. El siguiente método de controlador devuelve un flujo que contiene 5 elementos de karma. @GetMapping("/karma") public Flux<Karma> karma() { return prepareKarma() .map(Karma::new) .log(); } private Flux<Integer> prepareKarma() { Random random = new Random(); return Flux.fromStream( Stream.generate(() -> random.nextInt(10)) .limit(5)); } El método es una cosa crucial aquí. Observa todas las señales de Reactive Streams y las rastrea en registros bajo el nivel INFO. log La salida de registros en curl es la siguiente: localhost:8081/karma Como podemos ver, el procesamiento está ocurriendo en el grupo de subprocesos de IO. El nombre del subproceso significa . Las tareas se ejecutaron inmediatamente en un subproceso que las envió. Reactor no vio ninguna instrucción para programarlos en otro grupo. ctor-http-nio-2 reactor-http-nio-2 Retraso y procesamiento paralelo La siguiente operación retrasará la emisión de cada elemento en 100 ms (también conocido como emulación de base de datos) @GetMapping("/delayedKarma") public Flux<Karma> delayedKarma() { return karma() .delayElements(Duration.ofMillis(100)); } No necesitamos agregar el método aquí porque ya se declaró en la llamada original. log karma() En los logs podemos ver la siguiente imagen: Esta vez, solo se recibió el primer elemento en el subproceso IO . El procesamiento de los 4 restantes se dedicó a un grupo de subprocesos . reactor-http-nio-4 parallel Javadoc de confirma esto: delayElements Las señales se retrasan y continúan en el programador predeterminado paralelo Puede lograr el mismo efecto sin demora especificando en cualquier parte de la cadena de llamadas. .subscribeOn(Schedulers.parallel()) El uso del programador puede mejorar el rendimiento y la escalabilidad al permitir que varias tareas se ejecuten simultáneamente en diferentes subprocesos, lo que puede utilizar mejor los recursos de la CPU y manejar una gran cantidad de solicitudes simultáneas. parallel Sin embargo, también puede aumentar la complejidad del código y el uso de la memoria, y potencialmente provocar el agotamiento del grupo de subprocesos si se supera la cantidad máxima de subprocesos de trabajo. Por lo tanto, la decisión de utilizar un grupo de subprocesos debe basarse en los requisitos específicos y las ventajas y desventajas de la aplicación. parallel subcadena Ahora echemos un vistazo a un ejemplo más complejo. El código sigue siendo bastante simple y directo, pero el resultado es mucho más interesante. Vamos a usar un y hacer que un adivino sea más . Para cada instancia de Karma, multiplicará el ajuste original por 10 y generará los ajustes opuestos, creando efectivamente una transacción equilibrada que compensa la original. flatMap justo @GetMapping("/fairKarma") public Flux<Karma> fairKarma() { return delayedKarma() .flatMap(this::makeFair); } private Flux<Karma> makeFair(Karma original) { return Flux.just(new Karma(original.balanceAdjustment() * 10), new Karma(original.balanceAdjustment() * -10)) .subscribeOn(Schedulers.boundedElastic()) .log(); } Como puede ver, el flujo debe estar suscrito a un grupo de subprocesos . Veamos lo que tenemos en los registros para los dos primeros Karmas: makeFair's boundedElastic Reactor suscribe el primer elemento con en el subproceso IO balanceAdjustment=9 Luego, el funciona en la equidad de Karma emitiendo ajustes y en boundedElastic 90 -90 boundedElastic-1 Los elementos posteriores al primero se suscriben en un grupo de subprocesos paralelos (porque todavía tenemos en la cadena) delayedElements ¿ ? Qué es un programador boundedElastic Es un grupo de subprocesos que ajusta dinámicamente la cantidad de subprocesos de trabajo en función de la carga de trabajo. Está optimizado para tareas vinculadas a E/S, como consultas de bases de datos y solicitudes de red, y está diseñado para manejar una gran cantidad de tareas de corta duración sin crear demasiados subprocesos ni desperdiciar recursos. De forma predeterminada, el grupo de subprocesos un tamaño máximo de la cantidad de procesadores disponibles multiplicado por 10, pero puede configurarlo para usar un tamaño máximo diferente si es necesario. boundedElastic tiene Mediante el uso de un grupo de subprocesos asincrónicos , puede descargar tareas para separar subprocesos y liberar el subproceso principal para manejar otras solicitudes. La naturaleza limitada del grupo de subprocesos puede evitar la inanición de subprocesos y el uso excesivo de recursos, mientras que la elasticidad del grupo le permite ajustar la cantidad de subprocesos de trabajo dinámicamente en función de la carga de trabajo. boundedElastic Otros tipos de grupos de subprocesos Hay dos tipos más de grupos proporcionados por la clase lista para usar, como: Scheduler : este es un contexto de ejecución serializado de un solo subproceso que está diseñado para la ejecución síncrona. Es útil cuando necesita asegurarse de que una tarea se ejecute en orden y que no se ejecuten dos tareas al mismo tiempo. single : esta es una implementación trivial y sin operaciones de un programador que ejecuta tareas inmediatamente en el subproceso de llamada sin ningún cambio de subproceso. immediate Conclusión El modelo de subprocesamiento en Spring WebFlux está diseñado para que no bloquee y sea asincrónico, lo que permite el manejo eficiente de una gran cantidad de solicitudes con un uso mínimo de recursos. En lugar de depender de subprocesos dedicados por conexión, WebFlux utiliza una pequeña cantidad de subprocesos de bucle de eventos para manejar las solicitudes entrantes y distribuir el trabajo a los subprocesos de trabajo de varios grupos de subprocesos. Sin embargo, es importante elegir el grupo de subprocesos correcto para su caso de uso para evitar la escasez de subprocesos y garantizar un uso eficiente de los recursos del sistema.