Subproceso asíncrono @Async: la propia solución asíncrona de Spring

prefacio 

        En las aplicaciones de proyectos, el uso de llamadas asincrónicas de MQ para optimizar el rendimiento del sistema y completar la sincronización de datos entre servicios es un medio técnico común. Si está dentro del mismo servidor, no involucra un sistema distribuido y simplemente quiere implementar la ejecución asíncrona de algunos servicios, aquí hay una llamada de método asíncrono más simple.

        Para las llamadas a métodos asíncronos, la anotación @Async se proporciona desde Spring3, que se puede marcar en un método para llamar al método de forma asíncrona. La persona que llama regresará inmediatamente cuando se le llame, y la ejecución real del método se enviará a la tarea de Spring TaskExecutor, que será ejecutada por el subproceso en el grupo de subprocesos especificado.

        Este artículo describe la aplicación simple de la anotación @Async en el sistema Spring, solo con fines de aprendizaje, y agradece los comentarios.  


texto

1. Clasificación del grupo de hilos Spring

        Los siguientes son los cinco TaskExecuters comunes que se han implementado oficialmente. Spring afirma que estos TaskExecuters son perfectamente suficientes para cualquier escenario:

hilo características
SimpleAsyncTaskExecutor Cada vez que se solicita un nuevo subproceso, no hay una configuración para el número máximo de subprocesos. No es un grupo de subprocesos real, esta clase no reutiliza subprocesos, y cada llamada creará un nuevo subproceso .
SyncTaskExecutor Hilos no asíncronos. La sincronización puede usar SyncTaskExecutor, pero no se puede decir que sea un grupo de subprocesos, porque todavía se ejecuta en el subproceso original. Esta clase no implementa una llamada asíncrona, solo una operación síncrona.
ConcurrentTaskExecutorConcurrentTaskExecutor No se recomienda la clase de adaptación de Executor. Solo considere usar esta clase si ThreadPoolTaskExecutor no cumple con los requisitos.
SimpleThreadPoolTaskExecutor Es la clase SimpleThreadPool de Quartz. Esta clase es necesaria solo cuando el grupo de subprocesos es utilizado por cuarzo y no cuarzo.
ThreadPoolTaskExecutor El más utilizado y recomendado es la clase de subproceso especificada en la Especificación de desarrollo de Java de Alibaba, y se requiere que la versión jdk sea mayor o igual a 5. Su esencia es el empaquetado de java.util.concurrent.ThreadPoolExecutor.

       Consulte la especificación de desarrollo java de Alibaba. En la aplicación de grupo de subprocesos: el grupo de subprocesos no puede usar Ejecutores para crear, ni está permitido usar el grupo de subprocesos predeterminado del sistema. Se recomienda usar ThreadPoolExecutor. Este método de procesamiento hace que el Ingeniero de desarrollo más claro sobre las reglas de funcionamiento del grupo de subprocesos para evitar el riesgo de agotamiento de recursos.

2. Usa @Async en SpringBoot

        Usando un subproceso asíncrono para llamar a un método, el proceso es el siguiente:

  • Escriba una clase de configuración y defina un grupo de subprocesos
  • Anote el archivo de configuración/clase de inicio: @EnableAsync
  • Anote el método: @Async

        En el caso de demostración a continuación, el directorio de mi proyecto local es solo para referencia:

2.1 Habilitar @Async

        Anotación clave @EnableAsync ! ! ! Puede cargarse en la clase de inicio o agregarse al archivo de configuración, el efecto es el mismo.

  •  Método 1: habilitar según la clase de inicio de Springboot
@EnableAsync
@SpringBootApplication
public class AsyncApplication {

    public static void main(String[] args) {
        SpringApplication.run(AsyncApplication.class, args);
    }

}
  • Método 2: Habilitar según la configuración de Java
// com.example.async.service 为即将开启异步线程业务的包位置(后面有详细讲解)
@EnableAsync
@Configuration
@ComponentScan("com.example.async.service")
public class AsyncConfiguration implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return executor();
    }
    ...
}

2.2 @Async y grupo de subprocesos

        Spring aplica el grupo de subprocesos predeterminado, lo que significa que el nombre del grupo de subprocesos no se especifica cuando se usa la anotación @Async. Vea el código fuente, el grupo de subprocesos predeterminado de @Async es SimpleAsyncTaskExecutor .

@Slf4j
@Service
public class BusinessServiceImpl implements BusinessService {

    /**
     * 方法4:没有指定线程池,验证默认线程池也ok(不推荐:规避资源耗尽的风险)
     */
    @Async
    public void asyncDemo4() {
        log.info("asyncDemo4:" + Thread.currentThread().getName() + " 正在执行 ----------");
        try {
            Thread.sleep(2*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("asyncDemo4:" + Thread.currentThread().getName() + " 执行结束!!");
    }
}

2.3 Grupo de subprocesos personalizados @Async

        Personalice el grupo de subprocesos, que puede controlar el grupo de subprocesos en el sistema de manera más detallada, facilitar el ajuste de la configuración del tamaño del grupo de subprocesos y el control y procesamiento de excepciones de ejecución de subprocesos. Al configurar el grupo de subprocesos personalizado del sistema para reemplazar el grupo de subprocesos predeterminado, aunque se puede configurar en varios modos, solo se generará un grupo de subprocesos después de reemplazar el grupo de subprocesos predeterminado (no puede configurar varias clases para heredar AsyncConfigurer).

        El grupo de subprocesos personalizado tiene los siguientes modos:

  1. Reimplementar la interfaz AsyncConfigurer;
  2. Heredar AsyncConfigurerSupport;
  3. Configure un TaskExecutor personalizado para reemplazar el ejecutor de tareas incorporado;

        Los métodos de uso de los tres son aproximadamente los mismos, y el siguiente caso mostrará uno de ellos: la forma de implementar la interfaz AsyncConfigurer.

  • Configurar un grupo de subprocesos ThreadPoolTaskExecutor
/**
 * com.example.async.service:即将开启异步线程的业务方法是哪个
 *
 * 解释:
 *  1.即将开启异步线程业务的包位置:com.example.async.service
 *  2.通过 ThreadPoolExecutor 的方式,规避资源耗尽的风险
 */
@EnableAsync
@Configuration
@ComponentScan("com.example.async.service")
public class AsyncConfiguration implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return executor();
    }

    /**
     * 执行需要依赖线程池,这里就来配置一个线程池
     * 1.当池子大小小于corePoolSize,就新建线程,并处理请求
     * 2.当池子大小等于corePoolSize,把请求放入workQueue(QueueCapacity)中,池子里的空闲线程就去workQueue中取任务并处理
     * 3.当workQueue放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理
     * 4.当池子的线程数大于corePoolSize时,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁
     */
    @Bean("asyncExecutor")
    public ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //设置线程名
        executor.setThreadNamePrefix("async-method-execute-");
        //设置核心线程数
        executor.setCorePoolSize(10);
        //设置最大线程数
        executor.setMaxPoolSize(50);
        //线程池所使用的缓冲队列
        executor.setQueueCapacity(100);
        //设置多余线程等待的时间,单位:秒
        executor.setKeepAliveSeconds(10);
        // 初始化线程
        executor.initialize();
        return executor;
    }
}
  • Ejecute el método de subproceso asíncrono, especifique el grupo de subprocesos: el valor debe ser el mismo que el nombre en la clase de configuración Bean() 
/**
 * 异步线程 - 执行业务
 * 注意:
 *  1.@Async 注解调用用线程池,不指定的话默认:SimpleAsyncTaskExecutor
 *  2.SimpleAsyncTaskExecutor 不是真的线程池,这个类不重用线程,默认每次调用都会创建一个新的线程
 */
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
    /**
     * 方法1:@Async 标注为异步任务:执行此方法的时候,会单独开启线程来执行,不影响主线程的执行
     */
    @Async("asyncExecutor")
    public void asyncDemo1() {
        log.info("asyncDemo1:" + Thread.currentThread().getName() + " 正在执行 ----------");
        // 故意等10秒,那么异步线程开起来,这样明显看到:方法2不用等方法1执行完就调用了
        try {
            Thread.sleep(10*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("asyncDemo1:" + Thread.currentThread().getName() + " 执行结束!!");
    }

    /**
     * 方法2:与方法1一起执行,证明2个线程异步执行,互不干扰
     */
    @Async("asyncExecutor")
    public void asyncDemo2() {
        log.info("asyncDemo2:" + Thread.currentThread().getName() + " 正在执行 ----------");
        try {
            Thread.sleep(5*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("asyncDemo2:" + Thread.currentThread().getName() + " 执行结束!!");
    }

    /**
     * 方法3:没有指定线程池,验证默认线程池也ok(不推荐:规避资源耗尽的风险)
     */
    @Async
    public void asyncDemo3() {
        log.info("asyncDemo3:" + Thread.currentThread().getName() + " 正在执行 ----------");
        try {
            Thread.sleep(1*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("asyncDemo3:" + Thread.currentThread().getName() + " 执行结束!!");
    }
}

2.4 Comenzar la prueba

        Inicie el proyecto SpringBoot a través de AsyncApplication y Postman realizará pruebas de interfaz:

http://127.0.0.1:8080/async/demostración

  • Escribí 4 demostraciones para simular 4 situaciones respectivamente. Los detalles están escritos en los comentarios.
@Slf4j
@RestController
@RequestMapping("/async")
public class AsyncControllor {

    @Autowired
    private AsyncService asyncMethodService;
    @Autowired
    private BusinessService businessService;

    @GetMapping("/demo")
    public String demo()  {
        log.info("接口调用:【开始】 --------------------");
        try {
            // 执行异步任务 - 自定义线程池
            asyncMethodService.asyncDemo1();
            asyncMethodService.asyncDemo2();
            asyncMethodService.asyncDemo3();
            // 执行异步任务 - 默认线程池
            businessService.asyncDemo4();
        } catch (Exception e) {
            return "Exception";
        }
        log.info("接口调用:【结束】 --------------------");
        return "success";
    }
}
  • Resultado en ejecución: la ejecución de la interfaz finaliza y el subproceso asíncrono aún se está ejecutando


Resumir

  1. @EnableAsync es la clave para iniciar el subproceso asíncrono @Async, puede cargarse en la clase de inicio o agregarse al archivo de configuración;
  2. Para evitar el riesgo de agotamiento de recursos, se recomienda crear un grupo de subprocesos a través de ThreadPoolExecutor ;
  3. La anotación @Async se anota en el método para llamar al método de forma asíncrona;

Supongo que te gusta

Origin blog.csdn.net/weixin_44259720/article/details/130292656
Recomendado
Clasificación