找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1378

积分

0

好友

186

主题
发表于 2025-12-17 04:23:17 | 查看: 17| 回复: 0

在分布式系统开发中,我们经常遇到这样的场景:

  • 订单超时取消:用户下单后30分钟未支付,需要自动取消订单
  • 消息延迟推送:用户注册后,需要延迟5分钟发送欢迎消息
  • 异步回调处理:支付完成后,需要延迟确认订单状态

传统的解决方案往往存在以下痛点:

  1. 使用数据库轮询:频繁查询数据库,性能开销大,实时性差
  2. 使用消息队列延迟消息:缺乏在线治理能力,另外还需要额外维护消息队列
  3. 使用定时任务框架:功能单一,缺乏灵活的状态管理和监控

SnailJob Pro 延迟任务应运而生,它专门为解决这些痛点而设计,提供了一个灵活、可靠、高性能的分布式延迟任务调度平台。

SnailJob Pro 延迟任务的核心优势

1. 🚀 高性能调度引擎

SnailJob Pro 采用时间轮 + 分区模式实现,具备高度可伸缩性和容错性:

  • 秒级精度:支持秒级延迟任务调度,满足实时性要求
  • 高并发处理:分布式架构设计,轻松应对大规模任务调度
  • 低延迟:优化的调度算法,确保任务准时执行
  • 可扩展:分区存储设计,支持水平扩展

2. 📊 延迟跨度不再受限

  • 秒级
  • 分钟级
  • 小时级
    统一模型,无需区分“短延迟 / 长延迟”,避免为不同时间跨度设计不同实现方案。

3. 🎯 明确的任务语义,而不是“一条消息”

SnailJob Pro 明确区分任务类型:

  • 延迟任务(Delay Task):指定时间后执行一次
  • 回调任务(Callback Task):面向异步回调、补偿确认等场景
    任务是“可查询、可暂停、可恢复、可重试”的实体,而不是一次性投递行为。

4. 🔄 OpenAPI 动态治理能力

支持通过 OpenAPI:

  • 取消任务
  • 暂停 / 恢复任务
  • 立即触发执行
    延迟任务终于可以被“在线治理”,而不是只能靠日志猜状态。

5. 🛡️ 强大的容错机制

  • 自动重试:任务执行失败后,支持自动重试,可配置重试次数和重试策略
  • 超时保护:支持任务执行超时检查,防止任务长时间占用资源
  • 手动触发:支持手动触发等待中的任务,方便测试和紧急处理
  • 暂停/恢复:可以随时暂停任务,需要时再恢复执行
  • 执行记录:完整记录每次执行的结果,便于问题排查

6. 📈 可视化管理:延迟任务不再是黑盒

提供完整的 Web 管理界面:

  • 任务列表 & 条件筛选
  • 任务详情(参数 / 状态 /执行记录)
  • 批量操作
  • 实时状态监控

图片图片图片

7. 🔐 完善的权限管理

  • 支持细粒度的权限控制
  • 不同角色可以访问不同的功能模块
  • 操作日志完整记录,便于审计

使用指南:快速上手延迟任务

核心概念

在使用延迟任务之前,需要了解几个核心概念:

  • 延迟主题(DelayTopic):任务的业务主题,用于分类和路由,必须与 @DelayConsumer 注解中的 delayTopic 一致
  • 业务编号(BizNo):业务的唯一标识,通过 ExtraKeyEnum.BIZ_NO 设置,用于去重和查询
  • 触发时间(TriggerTime):任务的具体执行时间,使用 LocalDateTime 指定
  • 消费者(Consumer):使用 @DelayConsumer 注解标记的方法,用于处理延迟任务
  • 生产者(Producer):使用 DelayProducer 发送延迟任务
依赖配置

首先需要在项目中引入 SnailJob Pro 客户端依赖:

<dependency>
    <groupId>com.aizuda</groupId>
    <artifactId>snail-job-client-delay-core</artifactId>
    <version>最新版本</version>
</dependency>

然后在配置文件中配置 SnailJob 服务端地址:

snail-job:
  server:
    addr: localhost:8080 # SnailJob 服务端地址
  group: default-group   # 组名
依赖注入

使用 @RequiredArgsConstructor@Autowired 注入 DelayProducer

@Component
@RequiredArgsConstructor // Lombok 注解,自动生成构造函数
public class OrderService {
    private final DelayProducer delayProducer; // 自动注入
}

或者:

@Component
public class OrderService {
    @Autowired
    private DelayProducer delayProducer;
}

创建延迟任务

第一步:定义延迟任务消费者

使用 @DelayConsumer 注解定义延迟任务的消费者方法:

@Component
@Slf4j
@RequiredArgsConstructor
public class OrderDelayConsumer {
    /**
     * 订单超时取消的延迟任务消费者
     * @param delayArgs 延迟任务参数,包含业务数据
     * @return 执行结果
     */
    @DelayConsumer(delayTopic = "order-timeout-cancel")
    public DelayResult handleOrderTimeout(DelayArgs delayArgs) {
        log.info("处理订单超时取消,参数:{}", JSON.toJSONString(delayArgs));
        // 从 delayArgs 中获取业务参数
        String orderId = delayArgs.getArgsStr(); // 或根据实际参数结构解析
        try {
            // 执行订单取消逻辑
            orderService.cancelOrder(orderId);
            return DelayResult.success(); // 执行成功
        } catch (Exception e) {
            log.error("订单取消失败", e);
            return DelayResult.failure("订单取消失败", e.getMessage()); // 执行失败,会触发重试
        }
    }
}

关键点说明:

  • @DelayConsumer(delayTopic = "order-timeout-cancel"):指定延迟主题,用于路由任务
  • 方法必须返回 DelayResult,成功返回 DelayResult.success(),失败返回 DelayResult.failure()
  • DelayArgs 包含任务的所有参数信息,可通过 getArgsStr() 获取业务参数
第二步:发送延迟任务

使用 DelayProducer 发送延迟任务:

@Service
@RequiredArgsConstructor
public class OrderService {
    private final DelayProducer delayProducer;
    /**
     * 用户下单后,创建延迟取消任务
     */
    public void createOrder(String orderId) {
        // 创建订单逻辑...
        // 计算30分钟后的触发时间
        LocalDateTime triggerTime = LocalDateTime.now().plusMinutes(30);
        // 发送延迟任务
        delayProducer.sendAsync(
            "order-timeout-cancel", // 延迟主题,必须与 @DelayConsumer 中的 delayTopic 一致
            triggerTime,            // 延迟时间(通常与触发时间相同)
            new HashMap<String, String>() {{
                put(ExtraKeyEnum.BIZ_NO, "ORDER-" + orderId); // 业务编号,用于去重和查询
            }},
            new Runnable() {
                @Override
                public void run() {
                    // 任务提交成功后的回调
                    log.info("订单超时取消任务已提交,订单ID:{},触发时间:{}", orderId, triggerTime);
                }
            }
        );
    }
    /**
     * 用户支付成功后,取消延迟任务
     */
    public void payOrder(String orderId) {
        // 支付逻辑...
        // 取消对应的延迟任务(通过业务编号)
        delayProducer.cancel("order-timeout-cancel", "ORDER-" + orderId);
    }
}

API 说明:

  • sendAsync(delayTopic, delayTime, extraMap, callback)
    • delayTopic:延迟主题
    • delayTime:延迟时间(通常与触发时间相同)
    • extraMap:额外参数,如业务编号(BIZ_NO
    • callback:任务提交成功后的回调函数

任务管理

查询任务

在管理界面,可以通过以下条件查询任务:

  • 组名:筛选特定组的任务
  • 延迟主题:筛选特定主题的任务
  • 业务编号:精确查找某个业务的任务
  • 任务状态:按状态筛选(等待调度、调度中、成功等)
  • 时间范围:按创建时间或触发时间筛选
任务操作
  • 查看详情:点击任务ID,查看完整的任务信息
  • 手动触发:对于等待调度的任务,可以手动触发执行
  • 暂停任务:暂停正在等待或执行中的任务
  • 恢复任务:恢复已暂停的任务
  • 查看执行记录:查看任务的历史执行记录
  • 删除任务:删除不需要的任务

实际应用场景

场景一:延迟消息推送
// 1. 定义消费者
@Component
public class MessageDelayConsumer {
    @DelayConsumer(delayTopic = "welcome-message")
    public DelayResult sendWelcomeMessage(DelayArgs delayArgs) {
        String userId = delayArgs.getArgsStr();
        try {
            // 发送欢迎消息
            messageService.sendWelcomeMessage(userId);
            return DelayResult.success();
        } catch (Exception e) {
            log.error("发送欢迎消息失败", e);
            return DelayResult.failure("发送失败", e.getMessage());
        }
    }
}
// 2. 发送延迟任务
@Service
@RequiredArgsConstructor
public class UserService {
    private final DelayProducer delayProducer;
    public void registerUser(User user) {
        // 注册逻辑...
        // 延迟5分钟发送欢迎消息
        LocalDateTime sendTime = LocalDateTime.now().plusMinutes(5);
        delayProducer.sendAsync(
            "welcome-message",
            sendTime,
            new HashMap<String, String>() {{
                put(ExtraKeyEnum.BIZ_NO, "USER-" + user.getId());
            }},
            () -> log.info("欢迎消息任务已创建,用户ID:{}", user.getId())
        );
    }
}
场景二:任务执行失败重试
@Component
public class RetryDelayConsumer {
    @DelayConsumer(delayTopic = "retry-task")
    public DelayResult retryTask(DelayArgs delayArgs) {
        String taskId = delayArgs.getArgsStr();
        try {
            // 执行可能失败的任务
            boolean success = doSomething(taskId);
            if (success) {
                return DelayResult.success(); // 成功,不会重试
            } else {
                // 失败,返回失败结果,系统会自动重试
                return DelayResult.failure("任务执行失败", "业务逻辑返回失败");
            }
        } catch (Exception e) {
            // 异常,返回失败结果,系统会自动重试
            log.error("任务执行异常", e);
            return DelayResult.failure("任务执行异常", e.getMessage());
        }
    }
}

最佳实践

  1. 合理设置延迟主题:使用有意义的主题名称,便于管理和排查问题
  2. 业务编号唯一性:确保业务编号的唯一性,避免重复创建任务
  3. 灵活指定触发时间:使用 LocalDateTime 精确指定任务的触发时间
  4. 正确处理返回值
    • 成功:返回 DelayResult.success()
    • 失败:返回 DelayResult.failure(reason, message),系统会自动重试
  5. 异常处理:在消费者方法中做好异常捕获,返回失败结果而不是抛出异常
  6. 任务取消机制:对于可能被取消的任务,保存业务编号,及时取消延迟任务
  7. 监控告警:配置任务执行失败的告警,定期查看任务执行记录。对于复杂的分布式系统架构,完善的监控是保障稳定性的关键。
  8. 死信任务:执行失败的任务做到不丢失、具备回滚重新执行能力

总结

SnailJob Pro 延迟任务为Java后端开发带来的不是一个新工具,而是一种新能力,它极大地简化了分布式场景下的定时与延迟业务逻辑的实现:

  • 高性能调度
  • 失败可治理
  • 状态可观测
  • 操作可控制
  • 系统更稳定

通过将复杂的调度逻辑下沉到专门的平台,开发者可以更专注于核心业务代码,同时获得强大的任务运维与管理能力。




上一篇:Java高并发停车预约系统架构设计与场景应用分析
下一篇:支付行业格局重塑:银行、场景巨头与生态赋能者驱动商户服务迁移新趋势
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-25 00:47 , Processed in 0.161225 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表