关键是要分类隔离异常并策略响应:业务校验异常记录后继续,系统级异常立即中断,第三方异常可降级重试;改用for循环实现精准控制,失败订单归集至延迟队列由独立服务补偿。

遇到订单批量处理时个别失败、又不能让整个流程停摆的情况,关键不是“不让异常中断”,而是把异常分类、隔离、有策略地响应。for-each 本身不支持中断恢复或失败跳过,硬塞 try-catch 吞掉异常只会让问题隐形——日志没留、失败项没记、后续无法重试。真正可行的路径是:明确区分异常类型,按需阻断或放行,并为失败项预留补偿入口。
先分清三类异常,决定是否阻断
在遍历订单时,不是所有异常都该被“跳过”:
- 业务校验异常(如订单金额为负、客户已注销):属于预期内错误,应记录后继续,不中断遍历
- 系统级异常(如数据库连接超时、Redis 写入失败):说明环境不稳定,建议立即中断,避免雪球效应
- 第三方调用异常(如支付网关返回 503、短信平台限流):通常可降级或重试,不应直接中断主流程
用 for 循环替代 for-each,掌握主动权
for-each 是“只读遍历”,而订单处理需要写状态、记日志、控流程。改用传统 for 循环,能自由控制每一步:
- 每个订单单独 try-catch,异常不穿透到外层
- 成功时更新订单状态为 “processed”,失败时设为 “failed” 并存入失败队列
- 遇到系统级异常(如 SQLException),捕获后 break 并触发告警,停止后续处理
- 支持动态限流:比如每处理 10 单 sleep(50ms),防止压垮下游
失败订单统一归集,补偿逻辑外置
不要在循环里写重试或补偿,那会让逻辑缠绕、难以测试。推荐做法是:
- 定义一个 List
,每次捕获非阻断型异常时,封装订单 ID、失败原因、原始数据、时间戳后 add 进去 - 遍历结束后,检查该列表大小:为空则发成功通知;非空则写入 Redis 延迟队列或 Kafka 主题,交由独立补偿服务消费
- 补偿服务可实现指数退避重试、人工介入标记、失败超限自动转工单等策略
避免共享状态引发的连锁异常
多个订单异步处理时,若共用同一 Connection 或 HttpClient 实例,一个失败可能污染全局。务必做到:
- 数据库操作用 try-with-resources,确保 Connection/Statement 自动关闭
- HTTP 客户端使用连接池(如 Apache HttpClient 的 PoolingHttpClientConnectionManager),并设置 maxPerRoute
- 共享计数器(如成功总数)用 AtomicInteger,避免竞态
- 日志打点带唯一 traceId,方便按单追踪全链路










