在分布式系统开发中,我们经常遇到这样的场景:
- 订单超时取消:用户下单后30分钟未支付,需要自动取消订单
- 消息延迟推送:用户注册后,需要延迟5分钟发送欢迎消息
- 异步回调处理:支付完成后,需要延迟确认订单状态
传统的解决方案往往存在以下痛点:
- 使用数据库轮询:频繁查询数据库,性能开销大,实时性差
- 使用消息队列延迟消息:缺乏在线治理能力,另外还需要额外维护消息队列
- 使用定时任务框架:功能单一,缺乏灵活的状态管理和监控
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());
}
}
}
最佳实践
- 合理设置延迟主题:使用有意义的主题名称,便于管理和排查问题
- 业务编号唯一性:确保业务编号的唯一性,避免重复创建任务
- 灵活指定触发时间:使用
LocalDateTime 精确指定任务的触发时间
- 正确处理返回值:
- 成功:返回
DelayResult.success()
- 失败:返回
DelayResult.failure(reason, message),系统会自动重试
- 异常处理:在消费者方法中做好异常捕获,返回失败结果而不是抛出异常
- 任务取消机制:对于可能被取消的任务,保存业务编号,及时取消延迟任务
- 监控告警:配置任务执行失败的告警,定期查看任务执行记录。对于复杂的分布式系统架构,完善的监控是保障稳定性的关键。
- 死信任务:执行失败的任务做到不丢失、具备回滚重新执行能力
总结
SnailJob Pro 延迟任务为Java后端开发带来的不是一个新工具,而是一种新能力,它极大地简化了分布式场景下的定时与延迟业务逻辑的实现:
- ✅ 高性能调度
- ✅ 失败可治理
- ✅ 状态可观测
- ✅ 操作可控制
- ✅ 系统更稳定
通过将复杂的调度逻辑下沉到专门的平台,开发者可以更专注于核心业务代码,同时获得强大的任务运维与管理能力。