定时任务规范开发演变-函数式接口设为参数简化开发

本文介绍了如何使用函数式编程思想优化定时任务开发,通过函数式接口如Supplier和Function,结合lambda表达式和CompletableFutureAPI,简化了数据查询和处理逻辑。文章详细展示了如何创建一个工具类来封装线程池操作,以及如何使用装饰器模式和静态代理进行任务处理和日志记录。
摘要由CSDN通过智能技术生成

前言

一个理论:函数当参数:只传入逻辑不传入数据,参数由主方法提供

理论思想参考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> 参数接口 进行方法传递。
      • 静态代理模式
      • 装饰器模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值