Lambda表达式实现通用重试机制,通过Supplier/Function/Consumer封装操作,支持指数退避、日志记录、异常分类处理及幂等性要求,并可扩展自定义重试策略。

用 Lambda 表达式实现通用重试机制,核心是把“可能失败的操作”封装成可执行单元,再由重试工具统一控制执行次数、间隔和异常判断。它不依赖框架,轻量、泛型、可复用,特别适合调用 HTTP 接口、数据库查询、消息发送等易受网络或外部服务波动影响的场景。
封装可重试的操作逻辑
将不稳定操作抽象为函数式接口,比如 Supplier<R>(无参有返回)、Function<T, R>(带参有返回)或 Consumer<T>(带参无返回)。这样能覆盖大多数调用模式,且天然支持 Lambda 写法:
Supplier<String> apiCall = () -> httpClient.get("/user/123");Function<Long, User> dbQuery = id -> userDao.findById(id);Consumer<Order> mqSend = order -> kafkaTemplate.send("orders", order);
定义通用重试方法
写一个静态工具方法,接收上述函数式接口、重试次数、重试间隔(可选)和日志标识。关键点包括:
- 每次失败后记录日志,含第几次尝试、入参快照(如用
JSON.toJSONString())、异常类型 - 捕获所有
Exception,但避免吞掉InterruptedException或RuntimeException中需立即终止的类型(如OutOfMemoryError) - 支持指数退避:第 n 次重试前休眠
Math.min(1000 * (long) Math.pow(2, n-1), 30_000)毫秒(上限 30 秒) - 成功则立即返回结果;全部失败后按需返回
null、Optional.empty()或抛出包装异常
使用示例与注意事项
调用时只需一行 Lambda,清晰表达“我要做什么”和“失败了怎么办”:
String result = doWithRetry(() -> http.get("/status"), 3, 1000, "health-check");User user = doWithRetry(id -> userDao.findById(id), 2, 500, "db-user-query", 123L);
注意两点:
- 操作必须是幂等的——重复执行不能引发副作用(如重复扣款、重复发邮件),否则需配合唯一请求 ID 或状态校验
- 不要在 Lambda 里直接操作非 final 或局部变量;若需修改外部状态,改用
AtomicReference或包装类
进阶:支持条件重试与自定义策略
更灵活的做法是把重试决策外移,例如只对 IOException 或 HTTP 5xx 重试,而对 4xx 直接失败:
- 定义策略接口:
interface RetryPolicy { boolean shouldRetry(Throwable e, int attempt); long delayMs(int attempt); } - 在重试循环中调用
policy.shouldRetry(e, i)判断是否继续,再调用policy.delayMs(i)获取休眠时间 - 这样就能复用同一套重试骨架,适配不同服务的容错规则

















