在 Spring Boot 启动异步任务,用做异步处理

需求背景

最近在开发一个 邮箱系统,主要的工作是前端负责把要发送邮件的用户邮箱、邮件内容写入数据库中,然后由系统中的发送任务异步把这些邮件发送出去,所以需要有一个可一直在内部循环执行的 异步 任务进行处理

技术方案

对于像 php 这种程序,只需要在后台用过命令方式启动一个定时任务或者是循环任务,就可以完成。对于像 java 这种程序,可直接使用他的多线程来完成:

1. 异步任务 @Async


@Service
public class AsyncTestDomain {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Async
    public void task() {
        try {
            logger.info("[" + Thread.currentThread() + "] " + "任务开始");
            // 模拟任务延时
            Thread.sleep(3000);
            logger.info("[" + Thread.currentThread() + "] " + "任务结束");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}
@RestController
public class AsyncTestController {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    AsyncTestDomain asyncTestDomain;

    @GetMapping("/task/async")
    public String tetTask() {
        logger.info("[" + Thread.currentThread() + "] " + "任务请求开始");
        asyncTestDomain.task();
        logger.info("[" + Thread.currentThread() + "] " + "任务请求结束");
        return "ok";
    }
}

前端调用 controller:

curl "http://localhost:9999/task/async"

通过这种方式,调用由前端通过 http 调用来控制,可以配合 crontab 进行。

2. 使用定时任务处理

单线程任务/单任务

@Component
public class TaskSchedulerServer {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    Integer i = 0;

    @Scheduled(fixedRate = 1000) // 每隔1秒执行一次
    public void test() throws InterruptedException {
        String s = "[" + Thread.currentThread() + "] " + CommonUtil.currentTimeMillisSec();
        Thread.sleep(3000);
        logger.info(s + " - " + CommonUtil.currentTimeMillisSec() + " " + ++i);
    }
}

fixedRate:表示从任务开始的时间点起,每隔 5 秒执行一次。如果上一个任务还没完成,调度器会等待上一个任务结束,然后立即启动下一次任务。

fixedDelay:表示从任务完成的时间点起,延迟 5 秒再执行下一次任务。因此,如果一个任务执行花费 8 秒,下一次任务会在 8 秒后完成,然后再等 5 秒才会开始。

  1. 以上代码写好,springboot 运行起来后,test() 就会自动运行而且,而且进行频率由 fixedRate = 1000 控制;

  2. 该任务由系统内部自动运行,不受外部环境影响/干涉

  3. 该任务是系统默认的任务模式,同时也属于单线程

多线程任务/多任务

配置:

@Configuration
@EnableScheduling
public class MultiThreadSchedulerConfig {

    @Bean
    public TaskScheduler multiThreadTaskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(5);
        scheduler.setThreadNamePrefix("MultiThreadScheduledTask-");
        scheduler.initialize();
        return scheduler;
    }
}

使用:

如果没做其他处理,配置上面的多线程任务后,就默认生效,并且被使用了

同时使用单任务和多任务

单线程任务配置

@Configuration
@EnableScheduling
public class SchedulerConfig {
    // 使用默认的单线程调度器
    @Bean
    public TaskScheduler defaultTaskScheduler() {
        return new ConcurrentTaskScheduler(); // 单线程
    }
}

多线程任务配置(见上)

使用

@Component
public class TaskSchedulerServer {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    TaskScheduler multiThreadTaskScheduler; // 注入多线程调度器;

    Integer i = 0;

    @Scheduled(fixedRate = 1000) // 每隔5秒执行一次
    public void test() throws InterruptedException {
        String s = "[" + Thread.currentThread() + "] " + CommonUtil.currentTimeMillisSec();
        Thread.sleep(3000);
        logger.info(s + " - " + CommonUtil.currentTimeMillisSec() + " " + ++i);
    }

    @Scheduled(fixedRate = 1000) // 每隔5秒执行一次
    public void testMul() throws InterruptedException {
        multiThreadTaskScheduler.scheduleAtFixedRate(()-> {
            String s = "xxx[" + Thread.currentThread() + "] " + CommonUtil.currentTimeMillisSec();
            logger.info(s + " - " + CommonUtil.currentTimeMillisSec() + " " + ++i);
        }, 3000);
    }
}

发表评论