【码动四季】夏|SpringBoot 集成 Qdrant 报 UNAVAILABLE: No servers found 完整踩坑修复方案

发布时间:2026/7/18 16:27:48

【码动四季】夏|SpringBoot 集成 Qdrant 报 UNAVAILABLE: No servers found 完整踩坑修复方案 SpringBoot集成Qdrant报UNAVAILABLE: No servers found 完整踩坑修复方案一、问题现象本地单元测试单独调用Qdrant客户端完全正常SpringBoot项目启动后业务代码调用Qdrant向量库接口直接抛出gRPC异常io.grpc.StatusRuntimeException:UNAVAILABLE:Noservers foundfor127.0.0.1:6334网络层面验证Qdrant 服务正常监听0.0.0.0:6333/6334端口Windows 命令行Test-NetConnection 127.0.0.1 -Port 6334返回 TCP 连接成功无 xds、服务发现、round_robin 负载均衡相关自定义配置。二、问题根因1.双 Netty 传输依赖冲突项目同时存在两套 gRPC Netty 实现SpringBoot gRPC 组件传递引入 io.grpc:grpc-netty原生 nettyQdrant 客户端依赖 io.grpc:grpc-netty-shaded:1.51.0隔离版 netty。二者 SPI 优先级相同打包后优先加载原生 netty且 Qdrant 依赖排除原生 netty 类导致通道创建成功但无法建立连接。单元测试无 SpringBoot gRPC 依赖仅存在 shaded 包因此测试正常。2. Windows 下 gRPC DNS 解析器兼容问题即便强制使用 shaded 通道Windows 环境默认DnsNameResolver解析纯 IP 地址不稳定会返回空地址列表gRPC 判定无可用服务节点抛出No servers found。三、最终修复方案自定义静态 NameResolver核心思路抛弃默认 DNS 解析逻辑预解析 IP 生成固定地址列表绕过不稳定解析器同时显式使用 shaded Netty 构建通道隔离两套 netty 实现。importio.grpc.*;importio.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importio.qdrant.client.QdrantClient;importjava.net.InetAddress;importjava.net.InetSocketAddress;importjava.net.UnknownHostException;importjava.util.Collections;importjava.util.List;ConfigurationpublicclassQdrantConfig{Value(${qdrant.host:127.0.0.1})privateStringhost;Value(${qdrant.port:6334})privateintport;BeanpublicQdrantClientqdrantClient()throwsUnknownHostException{// 预解析IP地址InetAddressaddressInetAddress.getAllByName(host)[0];ListEquivalentAddressGroupaddressGroupsCollections.singletonList(newEquivalentAddressGroup(newInetSocketAddress(address,port)));// 自定义静态地址解析工厂NameResolver.FactoryfixedNameResolverFactorynewNameResolver.Factory(){OverridepublicNameResolvernewNameResolver(URItargetUri,NameResolver.Argsargs){returnnewNameResolver(){OverridepublicStringgetServiceAuthority(){returnhost:port;}Overridepublicvoidstart(Listener2listener){System.out.println(Qdrant静态解析地址列表addressGroups);listener.onAddresses(addressGroups,Attributes.EMPTY);}Overridepublicvoidshutdown(){}};}OverridepublicStringgetDefaultScheme(){returnfixed;}};// 使用shaded netty构建通道绑定自定义解析器与pick_first负载均衡ManagedChannelchannelNettyChannelBuilder.forTarget(fixed://host:port).nameResolverFactory(fixedNameResolverFactory).defaultLoadBalancingPolicy(pick_first).usePlaintext().keepAliveTime(10,TimeUnit.SECONDS).build();returnnewQdrantClient(channel);}}2. Service 层规范改造采用 lombok 构造注入清理冗余注解与无用导入importlombok.RequiredArgsConstructor;importio.qdrant.client.QdrantClient;importorg.springframework.stereotype.Service;ServiceRequiredArgsConstructorpublicclassQdrantServiceImpl{privatefinalQdrantClientqdrantClient;// 向量集合创建、检索、写入等业务方法}四、验证效果项目启动日志输出静态地址Qdrant静态解析地址列表[/127.0.0.1:6334]调用 Qdrant 所有接口不再抛出No servers found异常单元测试、SpringBoot 运行环境完全兼容项目内其他 gRPC 服务不受改动影响传输层完全解耦。五、方案取舍说明不推荐全局排除原生 grpc-netty直接排除grpc-netty虽能统一 shaded 包但 SpringBoot gRPC 服务端构建器强依赖原生 netty排除后会导致内部 gRPC 服务启动失败改动范围大、风险高。本方案仅针对 Qdrant 客户端单独隔离最小侵入业务。长期统一 netty 优化方案若后续项目全量使用 shaded netty可在父工程依赖管理统一管控 gRPC 版本通过配置grpc.netty.server.modeshaded切换全局传输实现可作为后续独立优化项。六、开发踩坑总结No servers found核心根源优先定位NameResolver地址解析结果而非 Netty 线程池Windows 本地开发gRPC 默认 DNS 解析器对纯 IP 存在兼容性缺陷Classpath 同时存在两套 gRPC Netty 依赖时典型特征为单测正常、运行报错TCP 连通不代表 gRPC 服务可用gRPC 拥有独立地址解析、负载均衡链路网络通用 Netty 冲突方案不一定适配向量库客户端请勿直接复制通用代码。七、application.yml 配置qdrant:host:127.0.0.1port:6334

相关新闻