批量处理框架 (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);
    }
}
相关推荐
花花鱼18 小时前
Spring Security 与 Spring MVC
java·spring·mvc
言慢行善19 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星19 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟20 小时前
操作系统之虚拟内存
java·服务器·网络
Tong Z20 小时前
常见的限流算法和实现原理
java·开发语言
凭君语未可20 小时前
Java 中的实现类是什么
java·开发语言
He少年20 小时前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
克里斯蒂亚诺更新20 小时前
myeclipse的pojie
java·ide·myeclipse
迷藏49420 小时前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构
迷藏49420 小时前
**发散创新:基于Solid协议的Web3.0去中心化身份认证系统实战解析**在Web3.
java·python·web3·去中心化·区块链