本文共 5257 字,大约阅读时间需要 17 分钟。
spring的内容比较多,常规的知识必须进行系统化的学习,但是一些边缘的技术点,在实际工作中也是非常适用的;下面一一介绍和实践一次。
场景:下发任务跟执行任务分开。
比如我需要做一个数据统计。
场景 | 常规做法 | 改进做法 |
---|---|---|
计算每天的统计数据,比如日新增,日活跃,日留存等 | 实时计算,计算和获取结果在同一个线程里完成 | 分两个部分:1.触发计算;2.异步完成计算; |
spring中如何实现异步计算
代码实例:
1.配置线程池,开启标记;
package com.springbootpractice.demo.spring.other.config;import com.google.common.util.concurrent.ThreadFactoryBuilder;import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.annotation.AsyncConfigurer;import org.springframework.scheduling.annotation.EnableAsync;import java.util.concurrent.Executor;import java.util.concurrent.LinkedBlockingDeque;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;/** * 说明:自定义异步线程池和异常处理 * @author carter * 创建时间: 2020年01月13日 10:12 上午 **/@EnableAsync@Configurationpublic class AsyncConfig implements AsyncConfigurer { /** * @return 线程池 */ @Override public Executor getAsyncExecutor() { return new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10000), new ThreadFactoryBuilder().setDaemon(true).setNameFormat("demo_spring_other_%s").build(), new ThreadPoolExecutor.DiscardPolicy() ); } /** * 可以结合监控系统,监控该异常,进行告警 * @return 异常处理 */ @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new SimpleAsyncUncaughtExceptionHandler(); }}
2.使用代码
package com.springbootpractice.demo.spring.other.service;import lombok.extern.slf4j.Slf4j;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Service;import java.time.LocalDate;import java.util.concurrent.TimeUnit;/** * 说明:统计数据计算业务代码 * @author carter * 创建时间: 2020年01月13日 10:27 上午 **/@Service@Slf4jpublic class AsyncSummaryDataService { @Async public void calculateDayNewData(LocalDate day) { try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } log.info("calculateDayNewData 执行线程:{}", Thread.currentThread().getName()); } @Async public void calculateDayLeftData(LocalDate day) { try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } log.info("calculateDayLeftData 执行线程:{}", Thread.currentThread().getName()); } @Async public void calculateDayActiveData(LocalDate day) { try { TimeUnit.SECONDS.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } log.info("calculateDayActiveData 执行线程:{}", Thread.currentThread().getName()); }}
@Autowired private AsyncSummaryDataService asyncSummaryDataService; @Test void asyncTest() { asyncSummaryDataService.calculateDayActiveData(LocalDate.now()); asyncSummaryDataService.calculateDayLeftData(LocalDate.now()); asyncSummaryDataService.calculateDayNewData(LocalDate.now()); }
输出:
2020-01-13 10:38:02.084 INFO 35059 --- [_spring_other_1] c.s.d.s.o.s.AsyncSummaryDataService : calculateDayLeftData 执行线程:demo_spring_other_12020-01-13 10:38:12.085 INFO 35059 --- [_spring_other_0] c.s.d.s.o.s.AsyncSummaryDataService : calculateDayActiveData 执行线程:demo_spring_other_02020-01-13 10:38:12.092 INFO 35059 --- [_spring_other_1] c.s.d.s.o.s.AsyncSummaryDataService : calculateDayNewData 执行线程:demo_spring_other_12020-01-13 10:38:12.108 INFO 35059 --- [ main] c.s.d.s.o.service.SummaryDataService : calculateDayActiveData 执行线程:main2020-01-13 10:38:32.114 INFO 35059 --- [ main] c.s.d.s.o.service.SummaryDataService : calculateDayLeftData 执行线程:main2020-01-13 10:38:42.117 INFO 35059 --- [ main] c.s.d.s.o.service.SummaryDataService : calculateDayNewData 执行线程:main
B端应用,可能需要一些定时任务,比如月末报表。在spring中启用定时任务非常简单,步骤如下:
使用步骤
配置项 | 说明 |
---|---|
cron | cron表达式 |
zone | 时区 |
fixedDelay,fixedDelayString | 固定延迟时间,两个任务之间的执行延迟;milliseconds |
fixedRate,fixedRateString | 固定频率,两个任务之间固定的执行间隔milliseconds |
initialDelay,initialDelayString | 初始化延迟,springIOC初始化完毕之后多少延迟之后开始执行首次任务的延迟milliseconds |
cron表达式不说了,可以直接使用工具生成: 推荐一个;
实例: 0 0 0 ? 每天0点开始执行;实例代码:
package com.springbootpractice.demo.spring.other.service;import lombok.extern.slf4j.Slf4j;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.scheduling.annotation.Schedules;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;/** * 说明:TODO * @author carter * 创建时间: 2020年01月13日 11:14 上午 **/@Service@Slf4jpublic class ScheduleTaskService { @Schedules({ @Scheduled(initialDelay = 5 * 1000, fixedRate = 10 * 1000) }) public void generateMonthReport() { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } log.info("执行月报表计算"); }}
1. 学会了使用spring内置的异步线程池来把某些同步方法改造成异步方法;
1. 学会了使用sping内置的定时任务来完成某些工作;
原创不易,转载请注明出处,欢迎多沟通交流