Quarkus事务超时场景下Mutiny@Retry注解不生效问题求助
解决Quarkus @Retry与Mutiny结合时事务超时不触发重试的问题
核心问题分析
- 方法可见性限制:你用
protected修饰recebeEvento和removerProcessamento方法,Quarkus的容错拦截器依赖CDI代理,protected方法无法被正确代理,导致@Retry注解完全不生效。 - 异常捕获范围不足:@Retry默认仅捕获
RuntimeException,而事务超时抛出的TransactionTimeoutException属于检查型异常,默认不会触发重试逻辑。 - 事务异常传播不彻底:同步事务操作的异常未被正确传递到Mutiny Uni的错误流中,重试机制无法感知到超时事件。
解决方案步骤
1. 修正方法可见性
将recebeEvento和removerProcessamento改为public,确保CDI拦截器能正常代理方法:
@Transactional @TransactionConfiguration(timeout = 30) @Incoming("ocr-in") @Retry(delay = 5000, maxRetries = 2, retryOn = {RuntimeException.class, TransactionTimeoutException.class}) public Uni<Void> recebeEvento(Message<String> ocr) { log.infof("OCR %s RECEBIDO", ocr.getPayload()); return getUni(ocr); } // ... public Uni<Void> removerProcessamento(Message<String> ocr) { log.errorf("ERRO O PROCESSAMENTO DO OCR %s, REMOVENDO REGISTROS", ocr.getPayload()); pecaService.limparProcessamentoOcr(Long.valueOf(ocr.getPayload())); return Uni.createFrom().item(ocr).replaceWith(() -> ocr.nack(new RuntimeException())).replaceWithVoid(); }
2. 扩展@Retry的异常捕获范围
在@Retry注解中显式指定retryOn属性,包含事务超时异常(根据你的依赖版本选择javax.transaction.TransactionTimeoutException或jakarta.transaction.TransactionTimeoutException):
@Retry(delay = 5000, maxRetries = 2, retryOn = {RuntimeException.class, TransactionTimeoutException.class})
3. 确保事务异常传递到Uni错误流
建议使用Mutiny响应式事务API替代传统@Transactional注解,让事务异常更可靠地被Uni捕获:
private Uni<Void> getUni(Message<String> ocr) { // 用Uni.createFrom().transactional包裹事务操作 return Uni.createFrom().transactional(() -> Uni.createFrom().item(ocr) .onItem().invoke(this::processar) .replaceWith(ocr::ack) .replaceWithVoid() ); }
同时可以移除recebeEvento方法上的@Transactional注解,由Mutiny统一管理事务生命周期。
4. 优化Fallback方法的响应式实现
将Fallback中的同步操作改为响应式,避免阻塞事件循环:
public Uni<Void> removerProcessamento(Message<String> ocr) { log.errorf("ERRO O PROCESSAMENTO DO OCR %s, REMOVENDO REGISTROS", ocr.getPayload()); return Uni.createFrom().item(ocr.getPayload()) .map(Long::valueOf) .call(pecaService::limparProcessamentoOcr) // 用call包裹同步服务调用 .invoke(() -> ocr.nack(new RuntimeException())) .replaceWithVoid(); }
验证要点
- 查看日志确认事务超时是否抛出
TransactionTimeoutException - 检查Quarkus日志中是否有重试相关输出(如
Retrying method...) - 验证消息的ack/nack逻辑在重试和fallback阶段正常执行
内容的提问来源于stack exchange,提问作者gravatasufoca
相关产品推荐
相关产品推荐

