XXL-JOB案例

wen java案例 2

本文目录导读:

XXL-JOB案例

  1. XXL-JOB基础介绍
  2. 环境搭建案例
  3. 实战案例
  4. 最佳实践建议

我来为您详细介绍XXL-JOB的实战案例,从基础到进阶,覆盖常见业务场景。

XXL-JOB基础介绍

XXL-JOB是一个分布式任务调度平台,核心特点:

  • 简单易用(通过Web页面管理任务)
  • 动态定时任务(支持CRON表达式)
  • 分布式调度(支持集群部署)
  • 失败重试机制
  • 任务分片处理

环境搭建案例

调度中心部署

-- 初始化数据库(执行官方提供的SQL脚本)
xxl_job.sql
-- 修改配置文件 application.properties
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root

执行器接入SpringBoot

<!-- pom.xml 引入依赖 -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.4.0</version>
</dependency>
# application.yml
xxl:
  job:
    admin:
      addresses: http://localhost:8080/xxl-job-admin
    executor:
      appname: xxl-job-executor-sample
      ip: 
      port: 9999
      logpath: /data/applogs/xxl-job/jobhandler
      logretentiondays: 30
// 执行器配置类
@Configuration
public class XxlJobConfig {
    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;
    @Value("${xxl.job.executor.appname}")
    private String appName;
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appName);
        xxlJobSpringExecutor.setPort(9999);
        xxlJobSpringExecutor.setLogPath("/data/applogs/xxl-job/jobhandler");
        return xxlJobSpringExecutor;
    }
}

实战案例

案例1:报表定时生成

业务场景:每天凌晨2点生成前一天的销售报表

@Component
public class ReportJobHandler {
    @Resource
    private SalesReportService reportService;
    @Resource
    private EmailService emailService;
    @XxlJob("dailySalesReportJob")
    public void dailySalesReport() throws Exception {
        // 获取任务参数(调度中心配置)
        String jobParam = XxlJobHelper.getJobParam();
        // 1. 获取前一天日期
        String yesterday = LocalDate.now().minusDays(1)
            .format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // 2. 查询销售数据
        List<SalesData> salesDataList = reportService.getDailySales(yesterday);
        // 3. 生成Excel报表
        Workbook workbook = ExcelGenerator.generateSalesReport(salesDataList);
        // 4. 发送邮件
        String[] receivers = {"manager@company.com", "finance@company.com"};
        emailService.sendReportEmail(receivers, 
            "业务报表-" + yesterday, workbook);
        // 5. 记录日志
        XxlJobHelper.log("报表生成成功,数据量:{}", salesDataList.size());
    }
}

案例2:订单超时处理(分片任务)

业务场景:每5分钟处理超时未支付的订单,使用分片提高效率

@Component
public class OrderTimeoutJobHandler {
    @Resource
    private OrderService orderService;
    @XxlJob("orderTimeoutHandleJob")
    public void handleTimeoutOrders() {
        // 获取分片参数
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();
        XxlJobHelper.log("开始处理超时订单,分片:{}/{}", 
            shardIndex, shardTotal);
        // 根据分片查询需要处理的订单
        // WHERE MOD(order_id, shardTotal) = shardIndex
        List<Order> timeoutOrders = orderService
            .getTimeoutOrdersForShard(shardIndex, shardTotal);
        for (Order order : timeoutOrders) {
            try {
                // 处理订单超时
                orderService.handleTimeoutOrder(order.getId());
            } catch (Exception e) {
                XxlJobHelper.log("订单{}处理失败:{}", 
                    order.getId(), e.getMessage());
            }
        }
        XxlJobHelper.log("分片{}处理完成,共处理{}个订单", 
            shardIndex, timeoutOrders.size());
    }
}

案例3:数据同步任务

业务场景:同步用户数据到搜索引擎

@Component
public class DataSyncJobHandler {
    @Resource
    private UserService userService;
    @Resource
    private ElasticSearchService esService;
    @XxlJob("userDataSyncJob")
    public void syncUserData() {
        // 获取增量同步游标
        String cursor = getLastSyncCursor();
        // 批量查询增量用户数据
        List<User> users = userService.getIncrementalUsers(cursor);
        if (users.isEmpty()) {
            XxlJobHelper.log("无增量数据需要同步");
            return;
        }
        // 批量同步到ES
        esService.bulkUpsertUsers(users);
        // 记录增量同步游标
        String lastUserId = users.get(users.size() - 1).getId();
        saveLastSyncCursor(lastUserId);
        XxlJobHelper.log("数据同步完成,同步用户数:{}", users.size());
    }
}

案例4:动态参数任务

业务场景:灵活执行数据清理任务,通过参数控制

@Component
public class DataCleanJobHandler {
    @XxlJob("dataCleanJob")
    public void cleanData() {
        // 从调度中心获取任务参数
        String jobParam = XxlJobHelper.getJobParam();
        // 解析任务参数:{"days":30,"table":"log","batchSize":1000}
        JSONObject paramObj = JSONObject.parseObject(jobParam);
        int days = paramObj.getIntValue("days");
        String table = paramObj.getString("table");
        int batchSize = paramObj.getIntValue("batchSize");
        // 计算清理截止时间
        Date deadline = DateTime.now()
            .minusDays(days)
            .toDate();
        int deletedCount = 0;
        while (true) {
            // 分批清理数据
            boolean success = dataCleanService.cleanOldData(
                table, deadline, batchSize);
            if (success) {
                deletedCount += batchSize;
                // 防止任务执行时间过长
                if (XxlJobHelper.getJobDuration() > 300000) {
                    XxlJobHelper.log("任务执行超时,提前结束");
                    break;
                }
            } else {
                break;
            }
        }
        XxlJobHelper.log("数据清理完成,共清理{}条数据", deletedCount);
    }
}

案例5:任务阻塞和失败处理

@Component
public class ComplexJobHandler {
    @XxlJob("complexJob")
    public ReturnT<String> complexTask(String param) {
        try {
            // 业务处理前检查
            if (!checkPreConditions()) {
                return new ReturnT<>(ReturnT.FAIL_CODE, 
                    "前置条件不满足,终止执行");
            }
            // 业务处理
            doBusinessLogic();
            // 可以通过抛出异常触发失败重试
            // throw new RuntimeException("模拟任务失败");
            return ReturnT.SUCCESS;
        } catch (Exception e) {
            XxlJobHelper.log("任务执行异常:{}", e);
            // 失败重试最多3次
            if (XxlJobHelper.getJobResult().getRetryCount() < 3) {
                return new ReturnT<>(ReturnT.FAIL_RETRY_CODE, 
                    "执行失败,等待重试");
            }
            return new ReturnT<>(ReturnT.FAIL_CODE, 
                "执行失败,超过重试次数");
        }
    }
    private void doBusinessLogic() {
        // 模拟业务处理
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

案例6:父子任务处理

业务场景:数据同步完成后再执行数据分析

@Component
public class ParentChildJobHandler {
    @Resource
    private JobClientUtils jobClientUtils;
    // 父任务
    @XxlJob("parentSyncJob")
    public void parentJob() {
        // 1. 执行数据同步
        syncData();
        // 2. 动态触发子任务
        try {
            jobClientUtils.triggerChildJob("childAnalyzeJob", 
                "syncData_completed", 1000);
        } catch (Exception e) {
            XxlJobHelper.log("触发子任务失败:{}", e);
        }
    }
    @JobHandler("childAnalyzeJob")
    public void childJob() {
        // 执行数据分析
    }
}

案例7:库存扣减任务

@Component
public class InventoryJobHandler {
    @Resource
    private InventoryService inventoryService;
    @XxlJob("inventoryDailyCheckJob")
    public void dailyInventoryCheck() {
        XxlJobHelper.log("=== 库存日结任务开始 ===");
        try {
            // 1. 获取所有库存
            List<Inventory> inventories = inventoryService.getAllInventory();
            // 2. 生成库存快照
            boolean snapshotSuccess = inventoryService.createInventorySnapshot();
            // 3. 库存预警检查
            List<String> warningProducts = new ArrayList<>();
            for (Inventory inv : inventories) {
                if (inv.getStock() < inv.getWarningThreshold()) {
                    warningProducts.add(inv.getProductId());
                }
            }
            // 4. 发送预警通知
            if (!warningProducts.isEmpty()) {
                sendStockWarningEmail(warningProducts);
            }
            XxlJobHelper.log("=== 库存日结任务完成,预警商品数:{} ===", 
                warningProducts.size());
        } catch (Exception e) {
            XxlJobHelper.log("库存日结任务失败:{}", e);
            throw new RuntimeException("库存日结失败", e);
        }
    }
}

最佳实践建议

调度中心配置参数

  • 阻塞处理策略:建议使用"丢弃后续调度"
  • 失败重试次数:建议2-3次
  • 日志保留天数:建议7-30天

任务开发规范

@XxlJob("yourJobName")  // 任务名称要规范,使用驼峰命名
public void yourJob() {
    // 1. 记录任务开始
    XxlJobHelper.log("任务开始执行");
    // 2. 获取参数(如果有)
    String param = XxlJobHelper.getJobParam();
    // 3. 业务处理
    try {
        // 业务逻辑
    } catch (Exception e) {
        // 4. 异常处理
        XxlJobHelper.log("任务执行异常:{}", e);
        throw e;  // 让调度中心知道任务失败
    }
    // 5. 记录任务结束
    XxlJobHelper.log("任务执行成功");
}

性能优化建议

  • 大数据量任务使用分片
  • 耗时操作异步处理
  • 批量操作设置合理批次大小
  • 避免任务执行时间过长(建议不超过10分钟)

监控告警建议

  • 配置任务失败告警邮件/短信
  • 定期检查执行器健康状态
  • 设置合理的任务执行超时时间
  • 监控调度中心负载情况

这些案例涵盖了XXL-JOB的主要使用场景,希望能帮助您更好地理解和使用XXL-JOB,记得根据实际业务需求调整和完善相关实现。

抱歉,评论功能暂时关闭!