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

Quarkus中注入Emitter失败:SRMSG00019通道连接异常求助

问题:Quarkus定时任务类无法注入AMQP Emitter,报错SRMSG00019

问题背景

基于Java 21的Quarkus 3.17应用中,使用Quartz的定时任务类注入AMQP的Emitter时失败,抛出错误jakarta.enterprise.inject.spi.DefinitionException: SRMSG00019: Unable to connect an emitter。但其他类注入同名Channel的Emitter完全正常,且该定时类中的ConfigMapping注入无异常。

问题类代码

package myproject.scheduling;

import java.util.List;

import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;
import io.quarkus.scheduler.Scheduled;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import myproject.config.ConnectionChecks;
import myproject.config.Constants.AmqpQueues.Producers;
import myproject.config.Constants.Jobs;
import myproject.models.measurements.ConnectionStatus;
import myproject.scheduling.jobs.ConnectionCheckRunner;

@ApplicationScoped
public class Scheduler {

    @Inject
    @Channel(Producers.MONITORING)
    Emitter<String> emitter;

    @Inject
    ConnectionChecks checks;

    @Scheduled(every = "1s", identity = Jobs.CONNECTION_CHECK)
    void scheduleInternetCheck() {
        List<ConnectionStatus> result = new ConnectionCheckRunner(checks).run();
        result.forEach((entry) -> System.out.println(entry.toLineProtocol()));
        result.forEach((entry) -> emitter.send(entry.toLineProtocol()));
    }
}

错误日志

2024-12-01 22:13:47,731 ERROR [io.qua.sch.com.run.StatusEmitterInvoker] (executor-thread-1) Error occurred while executing task for trigger io.quarkus.quartz.runtime.QuartzSchedulerImpl$QuartzTrigger@7f01115e: java.util.concurrent.CompletionException: java.lang.RuntimeException: Error injecting org.eclipse.microprofile.reactive.messaging.Emitter<java.lang.String> myproject.scheduling.Scheduler.emitter
    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:332)
    at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:347)
    at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:874)
    at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:887)
    at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2357)
    at java.base/java.util.concurrent.CompletableFuture$MinimalStage.whenComplete(CompletableFuture.java:2948)
    at io.quarkus.scheduler.common.runtime.DefaultInvoker.invoke(DefaultInvoker.java:25)
    at io.quarkus.scheduler.common.runtime.DelegateInvoker.invokeDelegate(DelegateInvoker.java:29)
    at io.quarkus.scheduler.common.runtime.StatusEmitterInvoker.invoke(StatusEmitterInvoker.java:35)
    at io.quarkus.scheduler.common.runtime.DelegateInvoker.invokeDelegate(DelegateInvoker.java:29)
    at io.quarkus.scheduler.common.runtime.DelegateInvoker.invokeComplete(DelegateInvoker.java:36)
    at io.quarkus.scheduler.common.runtime.OffloadingInvoker$2.call(OffloadingInvoker.java:54)
    at io.quarkus.scheduler.common.runtime.OffloadingInvoker$2.call(OffloadingInvoker.java:51)
    at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$4(ContextImpl.java:192)
    at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:270)
    at io.vertx.core.impl.ContextImpl$1.execute(ContextImpl.java:221)
    at io.vertx.core.impl.WorkerTask.run(WorkerTask.java:56)
    at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:642)
    at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
    at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
    at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
    at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
    at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.RuntimeException: Error injecting org.eclipse.microprofile.reactive.messaging.Emitter<java.lang.String> myproject.scheduling.Scheduler.emitter
    at myproject.scheduling.Scheduler_Bean.doCreate(Unknown Source)
    at myproject.scheduling.Scheduler_Bean.create(Unknown Source)
    at myproject.scheduling.Scheduler_Bean.create(Unknown Source)
    at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
    at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.c3(Unknown Source)
    at io.quarkus.arc.generator.Default_jakarta_enterprise_context_ApplicationScoped_ContextInstances.computeIfAbsent(Unknown Source)
    at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
    at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:21)
    at myproject.scheduling.Scheduler_ClientProxy.arc$delegate(Unknown Source)
    at myproject.scheduling.Scheduler_ClientProxy.scheduleInternetCheck(Unknown Source)
    at myproject.scheduling.Scheduler_ScheduledInvoker_scheduleInternetCheck_db9533208f2a6decf3d622882f85ce81424bd3a1.invokeBean(Unknown Source)
    ... 20 more
Caused by: jakarta.enterprise.inject.spi.DefinitionException: SRMSG00019: Unable to connect an emitter with the channel `monitoring-producer`
    at io.smallrye.reactive.messaging.providers.extension.ChannelProducer.getEmitter(ChannelProducer.java:225)
    at io.smallrye.reactive.messaging.providers.extension.ChannelProducer.produceEmitter(ChannelProducer.java:173)
    at io.smallrye.reactive.messaging.providers.extension.ChannelProducer_ProducerMethod_produceEmitter_jRfYFqSs8A2Ams_fdlkwttyVQ3w_Bean.doCreate(Unknown Source)
    at io.smallrye.reactive.messaging.providers.extension.ChannelProducer_ProducerMethod_produceEmitter_jRfYFqSs8A2Ams_fdlkwttyVQ3w_Bean.create(Unknown Source)
    at io.smallrye.reactive.messaging.providers.extension.ChannelProducer_ProducerMethod_produceEmitter_jRfYFqSs8A2Ams_fdlkwttyVQ3w_Bean.get(Unknown Source)
    at io.smallrye.reactive.messaging.providers.extension.ChannelProducer_ProducerMethod_produceEmitter_jRfYFqSs8A2Ams_fdlkwttyVQ3w_Bean.get(Unknown Source)
    at io.quarkus.arc.impl.CurrentInjectionPointProvider.get(CurrentInjectionPointProvider.java:48)
    ... 33 more

正常工作的类示例

package myproject.rest;

import java.util.Map;

import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;

import myproject.config.Constants.AmqpQueues.Producers;
import myproject.models.measurements.metrics.JobMetric;
import myproject.models.measurements.Data;
import io.smallrye.common.annotation.RunOnVirtualThread;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;

@Path("data")
@RunOnVirtualThread
public class DataResource extends Resource {

    @Inject
    @Channel(Producers.DATA)
    Emitter<String> emitter;

    @Inject
    @Channel(Producers.MONITORING)
    Emitter<String> metricEmitter;

    @POST
    public Map<String, String> postData(Data data) {
        JobMetric metric = startMetric("api_post_data");
        emitter.send(data.toLineProtocol());
        finishMetric(metric, metricEmitter);
        return getOkResponse("Uploaded data sucessfully");
    }

}

解决方案

方法1:添加虚拟线程注解

Quarkus默认用Vert.x Worker线程执行定时任务,而Reactive Messaging的Emitter依赖正确的CDI和Vert.x上下文。给定时任务类添加@RunOnVirtualThread注解,让任务在虚拟线程中执行(和正常工作的REST类一致),即可继承正确上下文完成注入:

@ApplicationScoped
@RunOnVirtualThread // 新增该注解
public class Scheduler {
    // ... 原有代码不变
}

方法2:手动获取Emitter

如果不想使用虚拟线程,可以在定时方法内通过CDI上下文手动获取Emitter,避免注入时机的上下文问题:

@ApplicationScoped
public class Scheduler {

    @Inject
    ConnectionChecks checks;

    @Scheduled(every = "1s", identity = Jobs.CONNECTION_CHECK)
    void scheduleInternetCheck() {
        // 手动获取Emitter
        Emitter<String> emitter = CDI.current()
                .select(Emitter.class, new Channel.Literal(Producers.MONITORING))
                .get();
        
        List<ConnectionStatus> result = new ConnectionCheckRunner(checks).run();
        result.forEach((entry) -> System.out.println(entry.toLineProtocol()));
        result.forEach((entry) -> emitter.send(entry.toLineProtocol()));
    }
}

内容的提问来源于stack exchange,提问作者JWo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:57:04