背景

在 Spring Cloud 微服务架构中,我们通常使用 RestTemplateWebClient 进行服务间的 HTTP 调用。随着 Spring 5 的推出,WebClient 成为了推荐的 HTTP 客户端,尤其是在响应式编程中。然而,在从 RestTemplate 升级到 WebClient 的过程中,我遇到了一个 DNS 解析失败的问题。

问题描述

在使用 RestTemplate 时,服务调用一切正常。但在切换到 WebClient 后,出现了以下错误:

1
org.springframework.web.reactive.function.client.WebClientRequestException: Failed to resolve 'DESKTOP-5JAE1TT.mshome.net' [A(1)] after 2 queries

从日志中可以看出,WebClient 成功通过负载均衡器获取到了服务实例的地址 http://DESKTOP-5JAE1TT.mshome.net:8001,但在实际请求时,DNS 解析失败,导致请求无法完成。


问题分析

1. RestTemplateWebClient 的差异

  • RestTemplate
    • 使用 Ribbon 作为负载均衡客户端。
    • Ribbon 会从服务发现(如 Consul)中获取服务实例列表,并直接使用实例的 IP 地址和端口进行请求,不会依赖 DNS 解析
    • 即使服务实例的主机名无法解析,Ribbon 仍然可以通过 IP 地址直接访问服务。
  • WebClient
    • 默认使用 Spring Cloud LoadBalancer。
    • 可能会直接使用 Consul 中注册的主机名。
    • 如果主机名无法解析,就会导致 DNS 解析失败。

2. 根本原因

  • DNS 解析失败
    • DESKTOP-5JAE1TT.mshome.net 是一个主机名,但网络环境无法解析该主机名。
    • 这可能是因为:
      1. 该主机名是本地网络中的计算机名称,无法在外部网络中解析。
      2. DNS 配置有问题,无法解析 .mshome.net 域名。

解决方法

1. 修改配置类

首先,我修改了 WebClient 的配置类,确保其支持负载均衡。

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class WebClientConfig {

@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}

@Bean
public WebClient webClient(WebClient.Builder builder) {
return builder.build();
}
}

通过这种方式,WebClient 能够正确集成 Spring Cloud LoadBalancer,支持通过服务名称解析实例地址。


2. 调试日志

为了进一步确认问题,我启用了调试日志,查看 WebClient 和负载均衡器的行为。

配置示例

1
2
3
4
5
logging:
level:
org.springframework.cloud: DEBUG
org.springframework.web.reactive: DEBUG
reactor.netty: DEBUG

通过日志,我确认了 WebClient 是否尝试使用主机名进行请求,并验证了接下来的解决方案的有效性。


3. 强制使用 IP 地址

为了彻底解决 DNS 解析问题,我修改了 cloud-provider-payment 服务的配置,使其在 Consul 中注册时使用 IP 地址,而不是主机名。

配置示例

1
2
3
4
5
6
spring:
cloud:
consul:
discovery:
prefer-ip-address: true # 使用 IP 地址注册服务
ip-address: 192.168.0.103 # 指定 IP 地址

这样,WebClient 在获取服务实例时,会直接使用 IP 地址,而不是主机名。


4. 添加 DNS 解析记录

其实到这里已经成功完成升级了,但是直接使用ip地址还是令人不太满意。还有一种更加简单的方式实现,可以通过修改本地 DNS 配置,将主机名映射到正确的 IP 地址。

修改 hosts 文件

  • cloud-consumer-order 服务所在的机器上,编辑 hosts 文件(路径:C:\Windows\System32\drivers\etc\hosts/etc/hosts)。

  • 添加以下内容:

    1
    127.0.0.1 DESKTOP-5JAE1TT.mshome.net

    127.0.0.1 替换为 DESKTOP-5JAE1TT.mshome.net 的实际 IP 地址。


总结

在从 RestTemplate 升级到 WebClient 的过程中,DNS 解析失败是一个常见问题。通过以下方法,我成功解决了这个问题:

  1. 修改配置类:确保 WebClient 支持负载均衡。
  2. 强制使用 IP 地址:修改服务注册配置,使用 IP 地址而不是主机名。
  3. 添加 DNS 解析记录:在本地 hosts 文件中添加主机名到 IP 地址的映射。
  4. 启用调试日志:通过日志确认问题根源和解决方案的有效性。

通过这些步骤,WebClient 的行为与 RestTemplate 一致,服务调用恢复正常。


参考文档