前言
一个理论:函数当参数:只传入逻辑不传入数据,参数由主方法提供
理论思想参考python 的 方法作为参数 ,从而引进lambda表达式
# 函数当参数只传入逻辑不传入数据
def conut(x, y):
return add(x, y)
def mian(count):
num1 = 1;
num2 = 2;
print(f"计算{num1},{num2} 结果{count(num1, num2)}")
由上期文章我们设计一个由函数式接口组成的工具类
定时任务规范开发演变-函数式接口+设计模式
但我们的开发可以继续优化,按照函数式编程思想定时任务可划分为是
- 主任务执行
- 查询批量数据方法
- 单个数据处理方法
规划
-
函数式接口
Supplier<T>
只能返回T格式数据Function<T, R>
传递T类型参数,返回R类型数据
-
定时任务规划
- 主任务执行
- 被控制层或者定时任务触发的方法,
- 普通方法
- 返回统计数据。
- 查询批量数据方法
- 查询一组需要处理的集合数据
- 由于设计了任务游标(防止任务意外中断后重新启动执行),执行方法可为 Supplier 或Function
- 返回数据集合。
- 单个数据处理方法
- 对单个数据进行逻辑处理
- 由于必须传入单个数据和统一的数据处理后结果返回值(用于数据统计)使用Function
- 返回数据处理结果。
- 统一数据处理返回值定义方法,游标取值定义方法
- 统一数据处理返回值id赋值,或用于游标的取值
- 由于可能处理的数据为类 使用Function 传递数据
- 返回需要的返回值id
- 主任务执行
主方法调用
@RestController()
@RequestMapping(value ="task" )
public class TaskController {
@Autowired
ITaskService taskService;
@RequestMapping(value = "/taskSimple",method = RequestMethod.GET)
public Map<String, Long> taskSimple() {
return taskService.taskSimple();
}
@RequestMapping(value = "/taskClassSimple",method = RequestMethod.GET)
public Map<String, Long> taskClassSimple() {
return taskService.taskClassSimple();
}
}
@Override
public Map<String, Long> taskSimple() {
//查询数据的方法
Function<Long, List> dataListFunction = (index)-> getBaseList(index, 100);
//执行数据处理的方法
Function dataFunction = (data)-> taskData((Long) data);
return ExecutorServiceTaskUtil.task(4,"taskSimple" ,dataListFunction,dataFunction,null);
}
@lombok.Data
class Data {
private Long id;
}
@Override
public Map<String, Long> taskClassSimple() {
//查询数据的方法
Function<Long, List> dataListFunction = (index)-> getDataList(index, 100);
//执行数据处理的方法
Function<Data,TaskResultResponse> dataFunction = (data)-> taskClassData(data);
//数据唯一标识方法
Function<Data, Long> resultDataFunction = Data::getId;
//执行任务
return ExecutorServiceTaskUtil.task(4,"taskClassSimple" ,dataListFunction,dataFunction,resultDataFunction);
}
task 为工具类暴露方法,定义线程池大小、线程名、数据查询方法、数据处理方法、返回值处理方法
- dataListFunction :查询数据集合逻辑
- dataFunction :数据处理逻辑。
- resultDataFunction :数据处理结果返回值逻辑。
定时任务工具类
@Slf4j
@Data
public class ExecutorServiceTaskUtil<T> {
//核心线程数
private Integer poolSize;
//线程名
private String threadPoolName;
//线程池
private ThreadPoolExecutor taskExecutor;
//任务情况统计
Map<String, Long> taskCountMap = Maps.newConcurrentMap();
//任务列表统计
Map<String, Long> taskListCountMap = Maps.newConcurrentMap();
//任务总数
private Long taskCount = 0L;
private Long successCount = 0L;
private Long failCount = 0L;
//开始时间
private Long startTime = 0L;
//结束时间
private Long endTime = 0L;
public ExecutorServiceTaskUtil(Integer poolSize, String threadPoolName) {
this.poolSize = poolSize;
this.threadPoolName = threadPoolName;
//定义线程池名 和 线程 抛出异常处理机制
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("timedTask-" +threadPoolName + "-%d").
setUncaughtExceptionHandler((thread, throwable)-> log.error("ThreadPool {} error",thread,throwable)).build();
// //定义全为核心线程,因为没有救急线程 long keepAliveTime, TimeUnit unit, 参数无意义
// //救急线程在定时的线程池的队列满的时候且核心线程都在执行时 才会创建
// ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(poolSize, poolSize,
// 1000L, TimeUnit.MILLISECONDS,
// new LinkedBlockingQueue<>(), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
this.taskExecutor = executor;
this.startTime = System.currentTimeMillis();
}
//Consumer 接口 做了处理器
public static ExecutorServiceTaskUtil execute(String taskName,int poolSize,Consumer<ExecutorServiceTaskUtil> consumer){
ExecutorServiceTaskUtil executorServiceTaskUtil = new ExecutorServiceTaskUtil(poolSize,taskName);
try {
consumer.accept(executorServiceTaskUtil);
} finally {
executorServiceTaskUtil.stop();
}
return executorServiceTaskUtil;
}
public static Map<String, Long> task(int poolSize, String taskName, Function<Long,List> dataListFunction, Function dataFunction,Function resultDataFunction) {
ExecutorServiceTaskUtil execute = ExecutorServiceTaskUtil.execute(taskName, poolSize,
(executorServiceTaskUtil) -> {
Long index = 0L;
List dataList = List.of();
for (Integer pageNum = 1; pageNum <= 100; pageNum++) {
try {
dataList = dataListFunction.apply(index);
} catch (Exception e) {
log.error("taskTemplate getBaseList error", e);
}
if (CollectionUtils.isEmpty(dataList)) {
break;
}
executorServiceTaskUtil.taskList(dataList, dataFunction,resultDataFunction);
//添加定时任务游标
index = Objects.isNull(resultDataFunction) ? (Long) dataList.get(dataList.size() - 1) : (Long) resultDataFunction.apply( dataList.get(dataList.size() - 1));
log.info("定时任务 {} 当前页数 {} 当前页数 {} 推送当前游标 {} listResult {}", "taskTemplate", pageNum,
dataList.size(), index, JSONUtil.toJsonStr(executorServiceTaskUtil.getTaskListCountMap()));
}
});
return execute.getTaskCountMap();
}
//supplier接口 做了包装器
public Supplier taskData(Function<T,TaskResultResponse> dataFunction,T data, Function resultDataFunction){
return () -> {
var resultData = Objects.isNull(resultDataFunction) ? data :resultDataFunction.apply(data);
TaskResultResponse<Object> result = new TaskResultResponse<>(resultData, true);
try {
result = dataFunction.apply(data);
log.info("{} taskData resultData {} result {}", threadPoolName, resultData, JSONUtil.toJsonStr(result));
} catch (Exception e) {
log.error("{} taskData 异常 resultData {}",threadPoolName, resultData, e);
result.setMsg("执行异常");
result.setSuccess(false);
}
return result;
};
}
//supplier接口 做了执行器
public void taskList(List<T> dataList, Function<T,TaskResultResponse> dataFunction,Function resultDataSupplier) {
if(dataList == null || dataList.size() == 0){
return;
}
int size = dataList.size();
long startTime = System.currentTimeMillis();
CompletableFuture<TaskResultResponse<Object>>[] callableArray = new CompletableFuture[size];
for (int i = 0; i < size; i++) {
//callableArray[i] = CompletableFuture.supplyAsync(dataSupplierList.get(i), taskExecutor).orTimeout(600,TimeUnit.SECONDS);
callableArray[i] = CompletableFuture.supplyAsync(taskData(dataFunction,dataList.get(i),resultDataSupplier), taskExecutor);
}
try {
//CompletableFuture.allOf(callableArray).orTimeout(600 * size, TimeUnit.SECONDS);
CompletableFuture.allOf(callableArray);
} catch (Exception e) {
log.error("{} CompletableFuture allOf error",threadPoolName ,e);
}
List<CompletableFuture<TaskResultResponse<Object>>> callableList = Lists.newArrayList(callableArray);
this.taskListCountMap = callableList.stream()
.collect(Collectors.groupingBy(e -> {
try {
return e.get().getMsg();
} catch (Exception ex) {
return "异步执行失败";
}
}, Collectors.counting()));
Map<Boolean, Long> collect = callableList.stream()
.collect(Collectors.groupingBy(e -> {
try {
return e.get().getSuccess();
} catch (Exception ex) {
return false;
}
}, Collectors.counting()));
addListCount();
Long falseListCount = collect.getOrDefault(false, 0L);
Long successListCount = collect.getOrDefault(true, 0L);
taskCount +=size;
successCount += successListCount;
failCount += falseListCount;
taskCountMap.put("任务总数",taskCount);
taskCountMap.put("任务成功数",successCount);
taskCountMap.put("任务过滤数",failCount);
taskListCountMap.put("任务列表总数", Long.valueOf(size));
taskListCountMap.put("任务列表成功数",successListCount);
taskListCountMap.put("任务列表过滤数",falseListCount);
log.info("{} taskList time {}s" , threadPoolName , (System.currentTimeMillis() - startTime)/1000 );
}
private void addListCount(){
if(taskListCountMap == null ||taskListCountMap.size() == 0){
return ;
}
for (String key : taskListCountMap.keySet()) {
Long oldValue = taskCountMap.get(key);
// 键不存在,将键值对添加到 ConcurrentHashMap 中
if (oldValue == null) {
taskCountMap.putIfAbsent(key, 0L);
oldValue = 0L;
}
Long value = taskListCountMap.get(key);
// 键存在,将键对应的值自增 1
while (true) {
Long newValue = oldValue + value;
if (taskCountMap.replace(key, oldValue, newValue)) {
// 替换成功,退出循环
break;
} else {
// 替换失败,重新获取最新值并重试
oldValue = taskCountMap.get(key);
}
}
}
}
public void stop() {
//执行后不再接收新任务,如果里面有任务,就执行完
taskExecutor.shutdown();
try {
//通常shutdown之后调用awaitTermination,作用是:后者会阻塞当前线程,等待剩余任务执行完,然后继续往下执行。如果不适用await,那么shutdown之后,很可能导致剩余任务得不到执行(整个程序退出),或是执行出现异常(某些资源被释放之类)。
if (!taskExecutor.awaitTermination(1000, TimeUnit.MINUTES)) {
//执行后不再接受新任务,如果有等待任务,移出队列;有正在执行的,尝试停止 超时的时候向线程池中所有的线程发出中断(interrupted)。
taskExecutor.shutdownNow();
}
} catch (InterruptedException ignore) {
taskExecutor.shutdownNow();
}
this.endTime = System.currentTimeMillis();
log.info("{} time {}s" , threadPoolName , (startTime - endTime)/1000 );
}
}
@Data
public class TaskResultResponse<T> {
private T data;
private Boolean success;
private String msg;
public TaskResultResponse(T t, Boolean success) {
this.success = success;
if (success) {
this.msg = "执行成功";
} else {
this.msg = "执行失败";
}
this.data = t;
}
}
使用了 CompletableFutureApi
处理线程任务,通过函数式接口处理通用代码,通用日志。
- 优点 :不需要知道如何做定时任务,只需要定义好数据处理方法和数据查询逻辑即可
代码解读
为什么要这样写代码?
ExecutorServiceTaskUtil execute = ExecutorServiceTaskUtil.execute(4,"taskTemplate",
(ExecutorServiceTaskUtil executorServiceTaskUtil) -> {
//定时任务处理逻辑
executorServiceTaskUtil.taskList(
dataList.stream().map((cid) -> executorServiceTaskUtil.taskData(
() -> 单个数据处理逻辑, cid)
).collect(Collectors.toList()));
})
定时任务工具类在代码中的集成为以上逻辑
- 定时任务执行:
public static ExecutorServiceTaskUtil execute(String taskName,int poolSize,Consumer<ExecutorServiceTaskUtil> consumer)
- 不希望使用者定义太多线程池的参数,只需定义线程池的名字和线程数。
- 封闭对线程池的操作。
- 其次在定时任务处理逻辑中,会用到线程池工具类对象 通过
interface Consumer<T>
参数接口 进行初始化线程池并且传递到接口的实现方法中,实现对于线程池的初始化和销毁。- 装饰器模式
- 处理器模式
- 数据集合处理
taskList(List<T> dataList, Function<T,TaskResultResponse> dataFunction,Function resultDataSupplier)
目的进行任务处理,数据统计。CompletableFuture.supplyAsync(dataSupplierList.get(i), taskExecutor)
用于线程任务处理CompletableFuture.allOf(callableArray);
用于主线程阻塞- 进行数据统计
- 数据处理
taskData(Function<T,TaskResultResponse> dataFunction,T data, Function resultDataFunction)
- 进行日志打印
- 必须返回 TaskResultResponse 对象,目的用于数据统计。
- 通过
interface Function<T,TaskResultResponse>
参数接口 进行方法传递。- 静态代理模式
- 装饰器模式