确保核心业务数据不静默丢失,应避免discardpolicy和abortpolicy,首选callerrunspolicy+有界队列+限流,或自定义策略+redis持久化+定时补偿,构建提交可追踪、拒绝可捕获、执行可验证、失败可恢复的闭环。
要确保核心业务数据不静默丢失,拒绝策略不能选 discardpolicy(静默丢弃)或 abortpolicy(抛异常但未兜底),而应聚焦于“任务可追溯、可重入、可补偿”的设计。关键不在策略本身,而在策略如何与整体任务生命周期协同。
首选:CallerRunsPolicy + 有界队列 + 主动限流
这是最轻量、最可控的方案,适用于下单、支付、库存扣减等强一致性场景。
- 用
ArrayBlockingQueue(非无界)控制内存水位,避免 OOM;容量建议设为 50–200,结合压测确定 - 搭配
CallerRunsPolicy:线程池满时,由调用线程(如 Tomcat 的 worker 线程)同步执行任务,不丢、不抛、不静默 - 必须配合上游限流(如 Sentinel 或 Spring Cloud Gateway 的请求速率控制),否则调用线程持续阻塞会导致接口雪崩
- 注意:该策略下任务执行延迟会升高,需监控 P99 响应时间,避免用户端超时重试造成重复提交
高可靠方案:自定义拒绝策略 + Redis 持久化 + 定时补偿
适合金融对账、订单履约、消息推送等“一次都不能少”的场景。
- 拒绝策略中不执行任务,而是将
Runnable序列化为 JSON,存入 Redis List 或 Stream(带唯一 ID 和时间戳) - 使用 Spring @Scheduled 每 3–5 秒扫描 Redis,将待处理任务重新 submit 到线程池(需判断线程池是否活跃)
- 任务类实现
PersistableTask接口,含getTaskId()方法,用于幂等校验(Redis 中已成功执行的任务不再入池) - Redis 设置过期时间(如 72 小时),并搭配后台任务清理失败超 3 次的任务,转入人工核查队列
兜底增强:DiscardOldestPolicy + 业务层重试 + 状态快照
作为辅助手段,不单独使用,但可降低极端情况下的数据缺口。
- 当选用
DiscardOldestPolicy时,被丢的是队列里最老任务,而非最新请求——这要求业务能容忍“稍旧数据延迟处理”,但绝不接受“最新数据消失” - 因此必须在提交任务前,先将关键参数(如订单号、用户ID、操作类型)落库标记为 “待处理” 状态
- 若后续发现该订单无处理日志,触发异步校验任务,从 DB 或日志中还原上下文,重新构造并提交任务
- 该方式依赖完备的状态记录和可观测性,不适合无状态或日志缺失的遗留系统
本质上,防止静默丢失不是挑一个“看起来安全”的策略,而是构建“提交可追踪、拒绝可捕获、执行可验证、失败可恢复”的闭环。策略只是其中一环,缺了持久化、幂等、监控、补偿中的任一环节,都可能让任务在无人知晓的情况下消失。











