- 前言
- 代码
- 一、业务处理
- 二、线程阻塞问题处理-AOP
- 三、Feign调用
- 四、立即执行的控制器接口
- 总结
提示:这里可以添加本文要记录的大概内容
问题背景:本来想用XXL-JOB进行定时任务处理,但是其目前只有MySQL版本,迁移到 SQL server我又嫌麻烦,所以利用Spring中的@Scheduled进行简单的定时任务的处理。
代码坑点:Spring中的Cron不支持年份,如果通过在线Cron表达式生成的需要把年份列删除,然而实际环境中也很少会用到年份。
思路拆解后主要是四类
- 业务处理
- 线程问题处理/SpringCron缺点处理
- Feign调用
- 立即执行的控制器接口
DemoBusiness示例代码
import org.springblade.aop.TaskThreadHandle;
import org.springframework.scheduling.annotation.Scheduled;
@Component
@EnableScheduling
public class DemoBusiness {
@Scheduled(cron = "0/5 * * * * *")
@TaskThreadHandle
public void demoRunSync() throws InterruptedException {
long startTime = System.currentTimeMillis();
Thread.sleep(10000);
System.out.println("=====>>>>>使用cron demoRunSync() {}");
long lastTime = System.currentTimeMillis();
System.out.println("demoRunSync()执行时长 = " + (lastTime - startTime) / 1000 + " 秒");
}
}
这里有个缺点,如果当有一个线程太慢了或者阻塞了,会影响到其它进程的执行,为了解决这个问题点,我做了一个AOP切面+异步线程池,上面已经有了注释说明。
二、线程阻塞问题处理-AOP接口
public @interface TaskThreadHandle {
}
实现
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springblade.common.tool.ThreadPoolUtils;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Component
@Aspect
public class TaskThreadHandleImpl {
@Around("@annotation(org.springblade.aop.TaskThreadHandle)")
public Object asyncThreadExecute(ProceedingJoinPoint point) throws Throwable {
CompletableFuture.runAsync(() -> {
try {
point.proceed();
// 这里应该套一层超时 以后再说
} catch (Throwable e) {
throw new RuntimeException(e);
}
}, ThreadPoolUtils.getThreadPool());
// void 也不要返回值了
return null;
}
}
这个线程池的工具类
import java.util.concurrent.*;
public class ThreadPoolUtils {
private static ThreadPoolExecutor pool = null;
public static void execute(Runnable runnable) {
getThreadPool().execute(runnable);
}
public static Future submit(Callable callable) {
return getThreadPool().submit(callable);
}
public static synchronized ThreadPoolExecutor getThreadPool() {
if (pool == null) {
// 获取处理器数量
int cpuNum = Runtime.getRuntime().availableProcessors();
// 根据cpu数量,计算出合理的线程并发数
// 最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
int maximumPoolSize = cpuNum * 2 + 1;
// 七个参数
// 1. 核心线程数
// 2. 最大线程数
// 3. 空闲线程最大存活时间
// 4. 时间单位
// 5. 阻塞队列
// 6. 创建线程工厂
// 7. 拒绝策略
pool = new ThreadPoolExecutor(maximumPoolSize - 1,
maximumPoolSize,
5,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(50),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
return pool;
}
}
三、Feign调用
在微服务场景下,做定时任务的最终目标肯定是会调用到其它的微服务,所以我这里是这么写的。
import org.springblade.core.tool.utils.SpringUtil;
public class UnifyClient {
private static IOrderClient orderClient;
public static IOrderClient getOrderClient() {
if (orderClient == null) {
orderClient = SpringUtil.getBean(IOrderClient.class);
}
return orderClient;
}
}
四、立即执行的控制器接口
这里没写完,但是以后再补充吧
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
}
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容。



