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

Quarkus原生构建问题:未找到NameResolverProviders致etcd通信失败

问题描述

我有一个经充分测试的Quarkus微服务,使用最新版Quarkus 3.2.2.Final,尝试构建为native模式时出现问题,该问题在JVM模式下不存在。

启动原生微服务时,会收到警告:

"io.grpc.NameResolverRegistry","level":"WARNING","message":"No NameResolverProviders found via ServiceLoader, including for DNS. This is probably due to a broken build. If using ProGuard, check your configuration"

随后与etcd的通信失败,报错cannot find a NameResolver for ip:///localhost:2379,应用退出。

在JVM DEBUG模式下可见共注册了7个解析器。我尝试显式注册反射类,但无效果:

@RegisterForReflection(targets={
    IPNameResolver.class,
    HttpNameResolver.class,
    HttpResolverProvider.class,
    IPResolverProvider.class,
    DnsSrvNameResolver.class,
    NameResolverProvider.class,
    LoadBalancerProvider.class,
    DnsNameResolverProvider.class,
    NameResolverRegistry.class
})

我怀疑问题并非类未正确注册,而是原生模式下动态类加载的特性导致。构建过程中无警告,请问下一步该如何排查?

编辑1:已安装特性

Installed features: [cache, cdi, hibernate-validator, kafka-client, micrometer, mongodb-client, mongodb-panache, narayana-jta, quartz, reactive-routes, resteasy-reactive, resteasy-reactive-jackson, scheduler, security, security-properties-file, smallrye-context-propagation, smallrye-health, smallrye-jwt, smallrye-reactive-messaging, smallrye-reactive-messaging-kafka, vertx]

原生构建命令行中可见:-H:-UseServiceLoaderFeature

编辑2:堆栈跟踪

2023-07-27 15:50:24,930 ERROR [io.qua.run.Application] (main) Failed to start application (with profile [prod]): java.lang.RuntimeException: Failed to start quarkus
    at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)
    at io.quarkus.runtime.Application.start(Application.java:101)
    at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:111)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:71)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:124)
    at com.y.z.responsedispatcher.ResponseDispatcher.main(ResponseDispatcher.java:10)
Caused by: java.lang.IllegalArgumentException: cannot find a NameResolver for ip:///localhost:2379
    at io.grpc.internal.ManagedChannelImpl.getNameResolver(ManagedChannelImpl.java:742)
    at io.grpc.internal.ManagedChannelImpl.getNameResolver(ManagedChannelImpl.java:751)
    at io.grpc.internal.ManagedChannelImpl.<init>(ManagedChannelImpl.java:631)
    at io.grpc.internal.ManagedChannelImplBuilder.build(ManagedChannelImplBuilder.java:631)
    at io.grpc.internal.AbstractManagedChannelImplBuilder.build(AbstractManagedChannelImplBuilder.java:297)
    at io.vertx.grpc.VertxChannelBuilder.build(VertxChannelBuilder.java:309)
    at io.etcd.jetcd.impl.ClientConnectionManager.getChannel(ClientConnectionManager.java:70)
    at io.etcd.jetcd.impl.ClientConnectionManager.newStub(ClientConnectionManager.java:102)
    at io.etcd.jetcd.impl.WatchImpl.<init>(WatchImpl.java:68)
    at io.etcd.jetcd.impl.ClientImpl.lambda$new$5(ClientImpl.java:53)
    at io.etcd.jetcd.support.MemorizingClientSupplier.get(MemorizingClientSupplier.java:37)
    at io.etcd.jetcd.impl.ClientImpl.getWatchClient(ClientImpl.java:85)
    at y.z.etcd.commons.core.EtcdClient.watchValues(EtcdClient.java:118)
    at y.z.etcd.commons.core.EtcdClient.watchValues(EtcdClient.java:112)
    at y.z.etcd.commons.core.EtcdClient_ClientProxy.watchValues(Unknown Source)
    at y.z.etcd.commons.z.services.zEtcdCache.init(zEtcdCache.java:41)
    at y.z.etcd.commons.z.services.zEtcdCache_Bean.doCreate(Unknown Source)
    at y.z.etcd.commons.z.services.zEtcdCache_Bean.create(Unknown Source)
    at y.z.etcd.commons.z.services.zEtcdCache_Bean.create(Unknown Source)
    at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:113)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:37)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:34)
    at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26)
    at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
    at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:34)
    at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:21)
    at y.z.etcd.commons.z.services.zEtcdCache_ClientProxy.arc$delegate(Unknown Source)
    at y.z.etcd.commons.z.services.zEtcdCache_ClientProxy.arc_contextualInstance(Unknown Source)
    at y.z.etcd.commons.z.services.zEtcdCache_Observer_Synthetic_7e505f92dfea7e2f74425f463632933f0b546710.notify(Unknown Source)
    at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:346)
    at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:328)
    at io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:82)
    at io.quarkus.arc.runtime.ArcRecorder.fireLifecycleEvent(ArcRecorder.java:155)
    at io.quarkus.arc.runtime.ArcRecorder.handleLifecycleEvents(ArcRecorder.java:106)
    at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy_0(Unknown Source)
    at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy(Unknown Source)
    ... 7 more
解决方案

1. 修复-H:-UseServiceLoaderFeature参数问题

构建命令中的-H:-UseServiceLoaderFeature是核心问题——该参数禁用了GraalVM的ServiceLoader特性,而gRPC正是通过ServiceLoader加载NameResolverProvider实现类的。JVM模式下ServiceLoader可正常工作,但native模式下禁用该特性后,所有依赖ServiceLoader的动态加载都会失效,这就是为什么没有任何NameResolverProviders被注册的原因。

解决方法:移除构建命令中的-H:-UseServiceLoaderFeature参数。如果是通过Quarkus配置文件设置的,找到对应配置项删除即可。

2. 手动注册gRPC解析器(必须保留上述参数时)

如果因特殊需求必须保留-H:-UseServiceLoaderFeature,需要手动注册gRPC的NameResolverProvider实现:

  • 创建应用启动时的注册类:
import io.grpc.NameResolverRegistry;
import io.grpc.ipv4.IPv4NameResolverProvider;
import io.grpc.ipv6.IPv6NameResolverProvider;
import io.grpc.dns.DnsNameResolverProvider;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import io.quarkus.runtime.StartupEvent;

@ApplicationScoped
public class GrpcResolverRegistrar {
    void onStartup(@Observes StartupEvent event) {
        NameResolverRegistry registry = NameResolverRegistry.getDefaultRegistry();
        registry.register(IPv4NameResolverProvider.getInstance());
        registry.register(IPv6NameResolverProvider.getInstance());
        registry.register(DnsNameResolverProvider.getInstance());
        // 若需要其他解析器,比如HttpResolverProvider,可在此处补充注册
    }
}
  • 确保这些Provider类被包含进native镜像:通过@RegisterForReflection标注,或在quarkus.native.additional-build-args中添加参数:
--initialize-at-build-time=io.grpc.ipv4.IPv4NameResolverProvider,io.grpc.ipv6.IPv6NameResolverProvider,io.grpc.dns.DnsNameResolverProvider

3. 验证etcd客户端配置

etcd客户端使用的ip:///localhost:2379协议需要对应的IP NameResolver。确认已引入gRPC的IP resolver依赖(通常包含在grpc-netty或grpc-core中),且这些类在native构建时被正确包含。

4. 检查扩展兼容性

确保使用的etcd客户端(jetcd)与Quarkus 3.2.2.Final兼容,可尝试升级jetcd到最新版本,或查看Quarkus官方文档中关于gRPC和etcd的native支持说明。

5. 启用native构建详细日志

添加以下参数到native构建命令,查看哪些类未被正确注册,辅助定位问题:

-H:+ReportExceptionStackTraces -H:Log=registerResource:trace

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 10:04:55