Quarkus EventBus requestAndForget 日志报超时错误问题咨询
Quarkus 2.9.2.Final版本中使用EventBus组件,调用requestAndForget方法对接返回值为void、标注@ConsumeEvent的消费方法时,业务逻辑可正常执行,但日志会抛出如下超时异常:
OK
2022-06-07 09:44:04,064 ERROR [io.qua.mut.run.MutinyInfrastructure] (vert.x-eventloop-thread-1) Mutiny had to drop the following exception: (TIMEOUT,-1) Timed out after waiting 30000(ms) for a reply. address: __vertx.reply.3, repliedAddress: receivedSomeEvent
对应消费端代码:
@ApplicationScoped public class ConsumerManiac{ @ConsumeEvent(value = "receivedSomeEvent") public void consume(SomeEvent someEvent ) { System.out.println("OK"); } }
对应生产端REST接口代码:
public class SomeResource { private final EventBus eventBus; @Inject public SomeResource (EventBus eventBus) { this.eventBus = eventBus; } @POST public Response send(@Valid SomeEvent someEvent) { eventBus.requestAndForget("receivedSomeEvent", someEvent); return Response.accepted().build(); } }
如果把消费方法修改为返回任意值(比如返回String类型"ok"),日志中就不会出现上述异常:
@ApplicationScoped public class ConsumerManiac{ @ConsumeEvent(value = "receivedSomeEvent") public String consume(SomeEvent someEvent ) { System.out.println("OK"); return "ok"; } }
完整异常栈如下:
2022-06-07 09:44:04,064 ERROR [io.qua.mut.run.MutinyInfrastructure] (vert.x-eventloop-thread-1) Mutiny had to drop the following exception: (TIMEOUT,-1) Timed out after waiting 30000(ms) for a reply. address: __vertx.reply.3, repliedAddress: receivedSomeEvent
at io.vertx.core.eventbus.impl.ReplyHandler.handle(ReplyHandler.java:76)
at io.vertx.core.eventbus.impl.ReplyHandler.handle(ReplyHandler.java:24)
at io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:893)
at io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:860)
at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:50)
at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:168)
at io.vertx.core.impl.AbstractContext.emit(AbstractContext.java:53)
at io.vertx.core.impl.VertxImpl$InternalTimerHandler.run(VertxImpl.java:883)
at io.netty.util.concurrent.PromiseTask.runTask(PromiseTask.java:98)
at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:170)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
requestAndForget的语义是「发送请求、等待消费端回复,但业务代码不处理回复内容」,不是真正的发后即忘。调用该方法时,Vert.x EventBus会自动注册一个默认30s超时的回复监听器:
- 如果消费方法有返回值,Quarkus会自动将返回值作为应答发回生产端,监听器收到应答后自动注销,不会触发超时
- 如果消费方法返回值为
void,Quarkus默认不会发送应答,监听器等满30s就会抛出超时异常,和业务逻辑是否执行成功无关
两种方案都可以保留消费方法的void返回值,彻底消除日志报错:
- 方案一(推荐):生产端改用
send方法实现真正的发后即忘,语义完全匹配不需要等待回复的场景,修改后生产端发送代码为:eventBus.send("receivedSomeEvent", someEvent); - 方案二:如果一定要用
requestAndForget,在消费端的@ConsumeEvent注解上显式配置关闭应答,配置方式为:@ConsumeEvent(value = "receivedSomeEvent", acknowledgement = ConsumeEvent.Acknowledgement.NONE) public void consume(SomeEvent someEvent ) { System.out.println("OK"); }
内容的提问来源于stack exchange,提问作者pringi

