제이온

[Spring] Cómo usar @Async

Creado: 2024-04-25

Creado: 2024-04-25 22:33

Manejo Asíncrono de Java

Antes de examinar Spring @Async, es fundamental comprender los conceptos de sincrónico, asíncrono y multihilo. Asumiendo que ya conoce estos conceptos, exploremos el método de manejo asíncrono de Java puro a través del código. Si está familiarizado con los hilos de Java, puede omitir este capítulo.



Si creamos una función que simplemente recibe un mensaje y lo imprime utilizando un enfoque síncrono, el código se vería como el anterior. Si cambiamos esto a un método asíncrono multihilo, el código fuente se puede escribir de la siguiente manera.



Sin embargo, este método es muy peligroso porque no podemos controlar los hilos. Por ejemplo, si se realizan 10,000 llamadas simultáneamente, se deben crear 10,000 hilos en un período de tiempo muy corto. El costo de crear hilos no es bajo, por lo que afecta negativamente al rendimiento del programa e incluso puede provocar un error OOM. Por lo tanto, debemos implementar un grupo de hilos para administrar los hilos, y Java proporciona la clase ExecutorService.



Hemos limitado el número total de hilos a 10 y ahora podemos realizar correctamente el manejo asíncrono del método multihilo que queremos. Sin embargo, con este método, debemos aplicar el método submit() de ExecutorService a cada método que queremos manejar de forma asíncrona, lo que requiere modificaciones repetitivas. Es decir, si queremos cambiar un método escrito inicialmente con lógica sincrónica a asíncrono, es inevitable cambiar la lógica del método en sí.


Spring @Async

Método simple


Simplemente agregue la anotación @EnableAsync sobre la clase Application y la anotación @Async sobre el método de lógica sincrónica que desea manejar de forma asíncrona. Sin embargo, este método tiene el problema de no administrar los hilos. Esto se debe a que la configuración predeterminada de @Async está configurada para usar SimpleAsyncTaskExecutor, que no es un grupo de hilos, sino que simplemente crea hilos.


Método que utiliza un grupo de hilos

Primero, elimine @EnableAsync de la clase Application. Si la clase Application tiene la configuración @EnableAutoConfiguration o @SpringBootApplication, leerá la información del bean threadPoolTaskExecutor (que crearemos a continuación) de la clase SpringAsyncConfig configurada en tiempo de ejecución.



Podemos configurar los tamaños de core y max. En este caso, esperamos que se ejecute con el tamaño de core inicial y que el número de hilos aumente hasta el tamaño max si no puede procesar más trabajo, pero no es así.


Internamente, crea un LinkedBlockingQueue de tamaño Integer.MAX_VALUE y, si los hilos del tamaño de core no pueden procesar el trabajo, esperan en la cola. Si la cola está llena, crea hilos hasta el tamaño max para procesar el trabajo.


Si le resulta incómodo configurar el tamaño de la cola en Integer.MAX_VALUE, puede configurar queueCapacity. Con la configuración anterior, inicialmente se procesarán 3 hilos y, si la velocidad de procesamiento es lenta, el trabajo se colocará en una cola de 100 elementos, y si se reciben más solicitudes, se crearán hasta 30 hilos para procesar el trabajo.


Una vez que la configuración del grupo de hilos está completa, simplemente agregue el nombre del bean a los métodos anotados con @Async.



Si desea configurar varios tipos de grupos de hilos, cree varios métodos de creación de beans como threadPoolTaskExecutor() y, al configurar @Async, inserte el bean del grupo de hilos que desee.


Forma en que se devuelve el tipo según el tipo de retorno

Cuando no hay valor de retorno

Este es el caso en el que el método que debe manejarse de forma asíncrona no necesita devolver ningún resultado de procesamiento. En este caso, configure el tipo de retorno de la anotación @Async en void.


Cuando hay un valor de retorno

Puede utilizar los tipos Future, ListenableFuture y CompletableFuture como tipo de retorno. Puede incluir la forma de retorno del método asíncrono en new AsyncResult().


[Future]


future.get() tiene el rol de esperar hasta que llegue el resultado de la solicitud a través del bloqueo. Por lo tanto, se convierte en un método de bloqueo asíncrono, lo que resulta en un bajo rendimiento. Por lo general, Future no se usa.


[ListenableFuture]


ListenableFuture permite procesar tareas de forma no bloqueante a través de devoluciones de llamada. El primer parámetro del método addCallback() define el método de devolución de llamada de finalización de la tarea, y el segundo parámetro define el método de devolución de llamada de error de la tarea. Como referencia, dado que el número de hilos básicos del grupo de hilos está configurado en 3, puede ver que el mensaje "Task Start" se imprime 3 veces al principio.



[CompletableFuture]

Si bien ListenableFuture es suficiente para implementar la lógica sin bloqueo, si necesita una devolución de llamada dentro de una devolución de llamada, provoca un código muy complejo llamado infierno de devoluciones de llamada.


<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F9f152db8-c015-43cd-bf13-85c594f5f218%2FUntitled.png?table=block&id=268ac0bc-ca7b-4bcb-b11b-ac611a5038b2&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1455;" width="2000" height="1455"></span>


Por supuesto, dado que no trataremos CompletableFuture en detalle en esta ocasión, si tiene curiosidad sobre el código que maneja devoluciones de llamada complejas, consulte el enlace de origen a continuación.



Tiene una mejor legibilidad que la definición de devolución de llamada de ListenableFuture y realiza completamente la función sin bloqueo. Por lo tanto, se recomienda usar CompletableFuture cuando necesite un valor de retorno al usar @Async.


Ventajas de @Async

Los desarrolladores pueden escribir métodos de forma sincrónica y, si desean utilizarlos de forma asíncrona, simplemente agregar la anotación @Async sobre el método. Por lo tanto, puede crear un código bien mantenido para sincrónico y asíncrono.


Precauciones con @Async

Para utilizar la función @Async, debe declarar la anotación @EnableAsync, pero si no realiza ninguna configuración adicional, funcionará en modo proxy. Es decir, todos los métodos asíncronos que funcionan con la anotación @Async siguen las restricciones de Spring AOP. Para obtener más información, consulteesta publicación.


  • Incluso si agrega @Async a un método privado, AOP no funcionará.
  • Si los métodos dentro del mismo objeto se llaman entre sí, AOP no funcionará.


Fuentes

Comentarios0