Spring Boot/Cloud调用inventory-service报UnknownHostException
问题根因
该异常是Spring Boot 2.7.0 搭配 Spring Cloud 2021.0.x 版本栈下,WebClient + Netty + Eureka + Spring Cloud LoadBalancer 组合的典型适配问题,触发逻辑完全匹配描述场景:
- 服务调用方使用默认基于Netty实现的WebClient发起调用
- 被调用方配置
server.port=0随机端口+随机实例ID启动 - 单实例启动时,Spring Cloud LoadBalancer的服务实例列表默认懒加载,首次请求触发时缓存未完成实例列表拉取,请求链路直接绕过负载均衡逻辑,将服务名作为普通域名交给Netty原生DNS模块发起公网解析,最终抛出
java.net.UnknownHostException: Failed to resolve 'inventory-service' after 4 queries - 多实例启动时,Eureka注册表的多次变更事件会提前触发LoadBalancer缓存刷新,首次请求时服务实例列表已完成加载,因此调用可正常执行。
修复方案
按以下顺序配置即可解决问题:
1. 修正WebClient的Bean配置
必须通过带@LoadBalanced注解的WebClient.Builder构造实例,直接调用WebClient.create()静态方法不会注入负载均衡的服务名解析过滤器:
@Configuration public class WebClientConfig { // 注入带负载均衡能力的WebClient构造器 @Bean @LoadBalanced public WebClient.Builder loadBalancedWebClientBuilder() { // 强制使用JDK内置地址解析器,彻底绕过Netty原生DNS逻辑 HttpClient httpClient = HttpClient.create() .resolver(DefaultAddressResolverGroup.INSTANCE); return WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)); } }
业务类注入时使用构造器生成WebClient实例:
@Service public class OrderBizService { private final WebClient webClient; // 注入配置好的Builder构造实例 public OrderBizService(WebClient.Builder loadBalancedWebClientBuilder) { this.webClient = loadBalancedWebClientBuilder.build(); } // 调用示例 public InventoryInfo queryInventory(String skuId) { return webClient.get() .uri("http://inventory-service/api/inventory?skuId=" + skuId) .retrieve() .bodyToMono(InventoryInfo.class) .block(); } }
2. 添加调用方配置(order-service的application.yml)
关闭Netty原生DNS,调整LoadBalancer缓存加载策略,避免懒加载导致的解析绕过问题:
spring: cloud: loadbalancer: cache: enabled: true ttl: 5s capacity: 128 health-check: initial-delay: 1s interval: 5s webclient: reactor: client: dns: resolver: native: false eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ fetch-registry: true register-with-eureka: true # 启动后立即拉取注册表,关闭延迟拉取 initial-instance-info-replication-interval-seconds: 1
3. 调整被调用方配置(inventory-service的application.yml)
修正随机实例ID规则,缩短心跳同步间隔,让单实例启动后快速同步到Eureka注册表:
server: port: 0 spring: application: name: inventory-service eureka: instance: # 固定实例ID生成规则,避免单实例场景下元数据哈希不稳定 instance-id: ${spring.application.name}:${random.value} # 优先使用IP注册,避免主机名解析冲突 prefer-ip-address: true # 缩短心跳间隔,加速注册表同步 lease-renewal-interval-in-seconds: 5 lease-expiration-duration-in-seconds: 15 client: service-url: defaultZone: http://localhost:8761/eureka/ fetch-registry: true register-with-eureka: true
验证流程
- 启动Eureka服务端,等待启动完成
- 启动单实例inventory-service,访问Eureka控制台确认服务状态为UP
- 启动order-service发起接口调用,DNS解析异常消失
- 启动多个inventory-service实例,验证负载均衡逻辑正常生效
注意:所有微服务间调用不要硬编码IP+端口,统一使用服务名发起请求,由负载均衡组件完成实例选择。
内容的提问来源于stack exchange,提问作者PAA
相关产品推荐
相关产品推荐

