You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus事务超时场景下Mutiny@Retry注解不生效问题求助

解决Quarkus @Retry与Mutiny结合时事务超时不触发重试的问题

核心问题分析

  1. 方法可见性限制:你用protected修饰recebeEvento和removerProcessamento方法,Quarkus的容错拦截器依赖CDI代理,protected方法无法被正确代理,导致@Retry注解完全不生效。
  2. 异常捕获范围不足:@Retry默认仅捕获RuntimeException,而事务超时抛出的TransactionTimeoutException属于检查型异常,默认不会触发重试逻辑。
  3. 事务异常传播不彻底:同步事务操作的异常未被正确传递到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 07:33:40