批量处理框架 (Batch Processing Framework)

批量处理框架 (Batch Processing Framework)

一个基于游标的通用批量数据处理框架,采用策略模式设计,支持串行和并行处理模式,内置重试机制和窗口分页处理,适用于各种需要批量处理数据的业务场景。

📖 框架简介

批量处理框架提供了一个高度可扩展的批量数据处理解决方案,通过游标机制实现高效的数据分页,支持多种数据类型的游标(如自增ID、时间戳、业务编码等)。框架采用策略模式,将业务逻辑与框架逻辑完全分离,让开发者专注于业务实现。

✨ 核心特性

1. 灵活的游标分页处理

  • 基于泛型游标的分页机制,支持多种数据类型(ID、时间戳、业务编码等)
  • 支持断点续传,处理中断后可继续
  • 可配置的窗口大小(默认5000条)
  • 自动管理处理进度和游标更新

2. 串行与并行双模式

  • 串行模式:适合数据量小、需要严格顺序处理的场景
  • 并行模式:支持多线程并发处理,大幅提升处理效率
  • 智能线程池管理,支持外部线程池注入或自动创建

3. 强大的重试机制

  • 可配置的重试次数和退避时间
  • 串行和并行处理均支持重试
  • 自动处理异常,提高处理成功率
  • 支持中断异常的正确处理

4. 策略模式设计

  • 高度可扩展的架构设计
  • 策略工厂统一管理,支持多策略并存
  • 业务逻辑与框架逻辑完全分离
  • 支持自定义参数类

5. 智能的保存/更新机制

  • 自动区分新数据和更新数据
  • 支持批量保存和批量更新
  • 可自定义判断逻辑

6. 线程池管理优化

  • 支持外部线程池注入,复用项目已有线程池
  • 自动根据CPU核心数计算合理的线程数
  • 使用有界队列,避免OOM风险

🎯 适用场景

1. 数据迁移与同步

将数据从一个系统迁移到另一个系统,或保持多个系统间的数据同步。

优势

  • 支持断点续传,迁移中断后可继续
  • 并行处理大幅提升迁移速度
  • 重试机制保证数据完整性

示例场景

  • 数据库迁移(MySQL → PostgreSQL)
  • 数据仓库ETL处理
  • 多数据源数据同步

2. 批量数据加工处理

对大量数据进行批量计算、转换、清洗等操作。

优势

  • 窗口分页避免内存溢出
  • 并行处理提升处理效率
  • 支持长时间运行的批处理任务

示例场景

  • 订单数据批量计算
  • 用户画像批量更新
  • 报表数据批量生成
  • 数据清洗和标准化

3. 定时任务批量处理

定时执行批量任务,如每日对账、批量推送等。

优势

  • 可配置最大处理轮次,控制执行时间
  • 支持失败重试,提高任务成功率
  • 详细的日志记录,便于问题排查

示例场景

  • 每日对账任务
  • 批量消息推送
  • 批量数据校验
  • 定时数据归档

4. 数据修复与补偿

修复历史数据问题,或补偿失败的业务操作。

优势

  • 基于游标机制,可精确控制处理范围
  • 支持增量处理,只处理需要修复的数据
  • 重试机制确保修复成功

示例场景

  • 历史订单状态修复
  • 用户积分补偿
  • 数据一致性修复
  • 业务数据回滚

🏗️ 架构设计

核心组件

BatchProcessingFactory

策略工厂 - 统一管理策略
AbstractCursorBatchTaskStrategy

抽象策略 - 核心处理逻辑
process

统一入口
processSerialWindow

串行处理
processParallelWindow

并行处理
processOneBatch

单批次处理
RetryExecutor

重试执行器
ExecutorHolder

线程池管理

处理流程



串行模式
并行模式


开始
创建参数对象

BaseBatchParam<K>
注册策略到工厂
调用 process 方法
窗口分页循环
获取Key列表

loadKeysWithCursor
数据为空?
结束
判断处理模式
processSerialWindow

串行处理
processParallelWindow

并行处理
RetryExecutor.execute

重试执行
batchProcessTasks

批量处理任务
saveOrUpdateProcessedData

保存/更新数据
多线程并行处理

ExecutorHolder
每个线程执行

RetryExecutor.execute
batchProcessTasks

批量处理任务
saveOrUpdateProcessedData

保存/更新数据
汇总结果
更新游标

resolveNextCursor
达到最大轮次?

🚀 快速开始

1. 添加依赖

方式一:直接下载(最简单)
  1. 访问项目 Gitee 页面
  2. 点击右上角 克隆/下载下载 ZIP
  3. 解压到本地目录
方式二:Git 克隆(推荐)
bash 复制代码
# HTTPS 方式
git clone https://gitee.com/ItmeiCode/batch-processing-framework.git

步骤 1:安装到本地 Maven 仓库

下载项目后,在项目根目录执行:

bash 复制代码
mvn clean install

步骤 2:在你的项目中添加依赖

在你的项目的 pom.xml 中添加:

xml 复制代码
<dependency>
    <groupId>com.itmei</groupId>
    <artifactId>batch-processing-framework</artifactId>
    <version>1.0.0</version>
</dependency>

步骤 3:编译你的项目

bash 复制代码
mvn clean compile

2. 实现策略类

java 复制代码
@Component
public class OrderProcessStrategy 
    extends AbstractCursorBatchTaskStrategy<Order, Long, BaseBatchParam<Long>> {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private OrderService orderService;
    
    // 必须实现:根据游标获取Key列表
    @Override
    protected List<Long> loadKeysWithCursor(Long lastCursor, BaseBatchParam<Long> param) {
        // lastCursor 为上一次处理完成的游标(首次为 null)
        int windowSize = param.getWindowSize();
        return orderMapper.selectOrderIdsByCursor(lastCursor, windowSize);
    }
    
    // 必须实现:批量处理任务
    @Override
    protected List<Order> batchProcessTasks(List<Long> orderIds, BaseBatchParam<Long> param) {
        // 1. 查询订单数据
        List<Order> orders = orderMapper.selectByIds(orderIds);
        
        // 2. 业务处理逻辑
        return orders.stream()
            .map(order -> {
                order.setStatus("PROCESSED");
                order.setProcessTime(new Date());
                return order;
            })
            .collect(Collectors.toList());
    }
    
    // 可选:判断是否为新订单(用于区分保存和更新)
    @Override
    protected boolean isNewTask(Order order) {
        return order.getId() == null;
    }
    
    // 可选:批量保存
    @Override
    protected boolean saveBatch(List<Order> orders) {
        return orderService.saveBatch(orders);
    }
    
    // 可选:批量更新
    @Override
    protected boolean updateBatchById(List<Order> orders) {
        return orderService.updateBatchById(orders);
    }
}

3. 注册策略并执行

java 复制代码
@Service
public class OrderBatchService {
    
    @Autowired
    private OrderProcessStrategy orderStrategy;
    
    public void processOrders() {
        // 1. 注册策略
        BatchProcessingFactory.registerStrategy(orderStrategy);
        
        // 2. 创建参数(使用泛型,支持多种游标类型)
        BaseBatchParam<Long> param = new BaseBatchParam<>();
        param.setWindowSize(1000);
        param.setMaxRounds(100);
        param.setMaxRetryTimes(3);
        param.setRetryBackoffMs(500L);
        
        // 3. 串行处理
        int total = orderStrategy.process(param);
        System.out.println("处理完成,共处理: " + total + " 条订单");
        
        // 4. 或者并行处理
        param.enableParallel(8, 2000);  // 8个线程,窗口大小2000
        int total2 = orderStrategy.process(param);
        System.out.println("并行处理完成,共处理: " + total2 + " 条订单");
    }
}

📝 参数配置说明

BaseBatchParam 参数说明

参数 类型 默认值 说明
lastCursor K null 上一次处理完成的游标(首次为null)
windowSize Integer 5000 每次处理的记录数量(窗口大小)
maxRounds Integer Integer.MAX_VALUE 最大处理轮次,防止无限循环
parallel boolean false 是否启用并行处理
parallelThreads Integer 6 并行处理使用的线程数(最大为CPU核心数×2)
minParallelBatchSize Integer 500 并行处理时,单线程最小处理记录数
maxParallelBatchSize Integer 3000 并行处理时,单线程最大处理记录数
maxRetryTimes Integer 3 单个批次允许的最大重试次数
retryBackoffMs Long 500L 重试之间的等待时间(毫秒)

配置示例

java 复制代码
// 串行处理配置
BaseBatchParam<Long> param = new BaseBatchParam<>();
param.setWindowSize(1000);           // 每次处理1000条
param.setMaxRounds(50);              // 最多处理50轮
param.setMaxRetryTimes(3);           // 失败重试3次
param.setRetryBackoffMs(1000L);      // 重试间隔1秒

// 并行处理配置
BaseBatchParam<Long> param = new BaseBatchParam<>();
param.enableParallel(8, 5000);       // 8个线程,每次处理5000条
param.setMaxRetryTimes(5);           // 失败重试5次
param.setRetryBackoffMs(500L);       // 重试间隔500毫秒

🔧 线程池管理

方式一:使用外部线程池(推荐)

如果项目已有线程池,建议复用,避免重复创建:

java 复制代码
@Configuration
public class BatchProcessingConfig {
    
    @Bean
    public ExecutorService batchExecutor() {
        // 创建项目统一的线程池
        return new ThreadPoolExecutor(
            10, 10, 0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1000),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }
    
    @PostConstruct
    public void init() {
        // 注入到框架中
        ExecutorHolder.setExecutor(batchExecutor());
    }
}

方式二:框架自动创建(默认)

框架会自动根据CPU核心数计算合理的线程数(CPU核心数 × 2):

java 复制代码
// 直接使用,框架会自动创建线程池
ExecutorService executor = ExecutorHolder.get();

线程池关闭

java 复制代码
// 关闭框架创建的线程池(不会关闭外部注入的线程池)
ExecutorHolder.shutdown();

// 检查是否已关闭
boolean isShutdown = ExecutorHolder.isShutdown();

// 检查是否使用了外部线程池
boolean isExternal = ExecutorHolder.isExternalExecutor();

💡 最佳实践

1. 窗口大小选择

  • 小窗口(100-1000):适合处理逻辑复杂、内存占用大的场景
  • 中等窗口(1000-5000):适合大多数场景,平衡性能和内存
  • 大窗口(5000+):适合处理逻辑简单、需要高吞吐的场景

2. 并行处理建议

  • 线程数设置:框架会自动限制为 CPU核心数 × 2,建议使用默认值
  • 批次大小:并行模式下,单线程批次建议在500-3000之间
  • 适用场景:数据量大、处理逻辑独立、无严格顺序要求
  • 线程池管理:优先使用外部线程池,便于统一管理

3. 重试策略

  • 重试次数:根据业务重要性设置,一般3-5次
  • 退避时间:根据外部依赖的恢复时间设置,建议500ms-2s
  • 适用场景:网络调用、数据库操作、外部服务调用

4. 游标管理

  • 游标类型:支持多种类型(Long、Date、String等),根据业务选择
  • 游标更新:框架会自动更新游标,无需手动管理
  • 断点续传 :将 lastCursor 持久化到数据库,实现断点续传
  • 重置游标 :使用 param.resetOffset() 方法重置处理进度

5. 异常处理

  • 业务异常 :在 batchProcessTasks() 中处理业务异常
  • 系统异常:框架会自动重试,重试失败后抛出异常
  • 日志记录:框架已内置日志,建议在业务代码中也添加日志

6. 自定义参数类

java 复制代码
public class OrderBatchParam extends BaseBatchParam<Long> {
    private String orderStatus;      // 订单状态过滤
    private Date startDate;          // 开始日期
    private Date endDate;            // 结束日期
    
    // getter/setter...
}

🔧 高级用法

自定义游标解析

默认情况下,框架使用窗口的最后一个Key作为下一个游标。如果业务有特殊需求,可以重写:

java 复制代码
@Override
protected Long resolveNextCursor(List<Long> windowKeys, BaseBatchParam<Long> param) {
    // 自定义游标计算逻辑
    // 例如:取最大值
    return windowKeys.stream().max(Long::compareTo).orElse(null);
}

策略工厂使用

java 复制代码
// 注册策略
BatchProcessingFactory.registerStrategy(orderStrategy);

// 获取策略(需要先注册)
OrderProcessStrategy strategy = BatchProcessingFactory.getStrategy(OrderProcessStrategy.class);

// 执行处理
int result = strategy.process(param);

断点续传实现

java 复制代码
@Service
public class OrderBatchService {
    
    public void processOrdersWithResume() {
        // 1. 从数据库读取上次处理的游标
        Long lastCursor = orderCursorRepository.getLastCursor();
        
        // 2. 创建参数并设置游标
        BaseBatchParam<Long> param = new BaseBatchParam<>();
        param.setLastCursor(lastCursor);
        param.setWindowSize(1000);
        
        // 3. 执行处理
        int total = orderStrategy.process(param);
        
        // 4. 保存当前游标(如果需要)
        Long currentCursor = param.getLastCursor();
        orderCursorRepository.saveLastCursor(currentCursor);
    }
}
相关推荐
Coder_Boy_2 小时前
基于SpringAI企业级智能教学考试平台试卷管理模块全业务闭环方案
java·大数据·人工智能·spring boot·springboot
C雨后彩虹2 小时前
synchronized底层原理:JVM层面的锁实现
java·synchronized
筑梦之路2 小时前
Jenkins 构建部署多模块Java应用流水线参考——筑梦之路
java·运维·jenkins
秋风不问归客2 小时前
linux 网络相关命令 及常用场景
linux·服务器·网络
shayudiandian2 小时前
【Java】常用类
java
雨中飘荡的记忆2 小时前
MyBatis类型处理模块详解
java·mybatis
金牌归来发现妻女流落街头2 小时前
【线程池 + Socket 服务器】
java·运维·服务器·多线程
牛奶咖啡132 小时前
Linux文件快照备份工具rsnapshot的实践教程
linux·服务器·文件备份·文件快照备份·rsnapshot·定时备份本地或远程文件·查看指定命令的完整路径
wanghowie2 小时前
01.03 Spring核心|事务管理实战
java·后端·spring