基于注解@Async的SpringBoot异步调用

       应用场景:异步调用通常用在发短信、发送邮件、消息推送 、运维凌晨自动化操作等,这些场景实时性要求不高,大多都是推广统计等服务。

概述

SpringBoot 中通过线程池来异步执行任务的两种方法:

通过 Spring 自带的 @EnableAsync 和 @Async 两个注解实现异步执行任务功能

通过自定义的方式

在通过 @EnableAsync 和 @Async 两个注解实现异步执行任务中会进一步分析 @Async 的局限性,自定义 @Async 注解的线程池,以及异常的处理。

使用 spring boot 异步注解 @EnableAsync 和 @Async

@Async 的局限性

1.只能作用于 public 方法上

2.方法不能自己调自己,也就是说不能迭代调用

基本使用

在 AsyncService 中增加两个方法:一个有返回值,返回值为 Future 对象;一个没有,都通过 api 调用,具体如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.concurrent.Future;

/**
 * @packagb:
 * @Author: fab
 * @Description:
 * @Date:Create:in 2019/12/4
 * @Modified By:
 */
@Service
public class AsyncService {
    private Logger logger = LoggerFactory.getLogger(AsyncService.class);

    @Async
    public void asyncData() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.toString();
        }
        logger.info("Execute method asynchronously, thread name = {}", Thread.currentThread().getName());
    }

    @Async
    public Future asyncGetData() {
        logger.info("Execute method asynchronously, thread name = {}", Thread.currentThread().getName());
        try {
            Thread.sleep(5000);
            return new AsyncResult("执行结果");
        } catch (Exception e) {
            logger.error("");
            return new AsyncResult(null);
        }
    }
    
    @Async("selfConfigThreadPool")
    public void asyncSelfData() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.toString();
        }
        logger.info("Execute method asynchronously, thread name = {}", Thread.currentThread().getName());
    }

}

AsyncController 如下:



import com.hikvision.ga.common.BaseResult;
import com.hikvision.zl.modules.service.AsyncService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @packagb:
 * @Author: fab
 * @Description:
 * @Date:Create:in 2019/12/4
 * @Modified By:
 */
@Api
@RestController
@RequestMapping(value = "/api/async")
public class AsyncController {
    @Autowired
    private AsyncService dbService;

    /**
     * 请求,立即返回,但是不是具体的执行结果,具体的任务在线程池中慢慢的执行
     */
    @GetMapping("/asyncData")
    public void asyncData() {
        dbService.asyncData();
    }
    /**
     * 请求,执行完毕后再返回具体的结果,具体的任务在线程池中执行
     */
    @GetMapping("/asyncGetData")
    public void asyncGetData() throws Exception {
        System.out.println(dbService.asyncGetData().get());
    }
    @GetMapping("/asyncSelfData")
    public BaseResult asyncSelfData(){
        dbService.asyncSelfData();
        return  BaseResult.success(null);
    }

}

测试

http://127.0.0.1:9001/zlTestDemo/api/async/asyncData

对于 /api/async/asyncData 请求,立即返回,但是不是具体的执行结果,具体的任务在线程池中慢慢的执行,具体如下:

对于 /api/async/asyncGetData 请求,执行完毕后再返回具体的结果,具体的任务在线程池中执行,具体如下

从执行结果来看,AsyncService 中的 asyncData 方法和 asyncGetData 方法都执行在 SimpleAsyncTaskExecutor 线程池中

http://127.0.0.1:9001/zlTestDemo/api/async/asyncData

http://127.0.0.1:9001/zlTestDemo/api/async/asyncGetData

http://127.0.0.1:9001/zlTestDemo/api/async/asyncSelfData

自定义

新增配置类 ThreadConfig,通过 @Bean 来配置单个或者多个线程池。

ThreadConfig 配置类,定义了两个线程池,一个用来发送邮件,一个用来处理心跳服务



import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @packagb:
 * @Author: fab
 * @Description:
 * @Date:Create:in 2019/12/4
 * @Modified By:
 */
@Configuration
public class ThreadConfig {
    /**
     * 邮件服务
     * @return
     */
    @Bean("sendMailExecutorService")
    public ExecutorService sendMailExecutorService() {
        return new ThreadPoolExecutor(2, 2,
                60L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>());
    }
    /**
     * 心跳服务
     * @return
     */
    @Bean("heartbeatExecutorService")
    public ExecutorService heartbeatExecutorService() {
        return new ThreadPoolExecutor(1, 1,
                60L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>());
    }
}

通过 @Qualifier("sendMailExecutorService") 和 @Autowired 注入
package com.hikvision.zl.modules.service;
ThreadService
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @packagb:com.hikvision.zl.modules.service
 * @Author: fab
 * @Description:
 * @Date:Create:in 2019/12/4
 * @Modified By:
 */
@Service
public class ThreadService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Qualifier("sendMailExecutorService")
    @Autowired
    private ExecutorService sendMailExecutorService;

    @Qualifier("heartbeatExecutorService")
    @Autowired
    private ExecutorService heartbeatExecutorService;


    public void heartbeatService() {
        heartbeatExecutorService.submit(() -> {
            // TODO 负责心跳有关的工作
            logger.info("Execute heartbeatService asynchronously, thread name = {}", Thread.currentThread().getName());
        });
    }

    public Future sendMailService() {
        return sendMailExecutorService.submit(() -> {
            logger.info("Execute sendMailService asynchronously, thread name = {}", Thread.currentThread().getName());
            // 休息1秒,模拟邮件发送过程
            TimeUnit.SECONDS.sleep(1);
            return true;
        });
    }

}

注意 ThreadConfig 中配置了多个线程池,由于 ExecutorService 类型相同,因此需要加上 Bean 的名称以及在注入的时候需要加上 @Qualifier

通过 ThreadController 调用服务,具体如下

ThreadController 中 /api/asyncThread/heartbeat api 执行不需要返回,/api/asyncThread/sendMail api 需要返回结果



import com.hikvision.ga.common.BaseResult;
import com.hikvision.zl.modules.service.ThreadService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @packagb:
 * @Author: fab
 * @Description:
 * @Date:Create:in 2019/12/4
 * @Modified By:
 */
@Api
@RestController
@RequestMapping(value = "/api/asyncThread")
public class ThreadController {
    @Autowired
    private ThreadService threadService;

    /**
     * 请求,立即返回,但是不是具体的执行结果,具体的任务在线程池中慢慢的执行
     */
    @GetMapping("/heartbeat")
    public BaseResult asyncData() {
        threadService.heartbeatService();
        return BaseResult.success(null);
    }
    /**
     * 请求,执行完毕后再返回具体的结果,具体的任务在线程池中执行
     */
    @GetMapping("/sendMail")
    public BaseResult asyncGetData() throws Exception {
        return BaseResult.success(threadService.sendMailService().get());
    }

}

测试

http://127.0.0.1:9001/zlTestDemo/api/asyncThread/heartbeat

http://127.0.0.1:9001/zlTestDemo/api/asyncThread/sendMail

 

你可能感兴趣的:(SringBoot)