从 RestTemplate 升级到 WebClient:解决 DNS 解析失败问题
背景
在 Spring Cloud 微服务架构中,我们通常使用 RestTemplate
或 WebClient
进行服务间的 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. RestTemplate
和 WebClient
的差异
RestTemplate
- 使用 Ribbon 作为负载均衡客户端。
- Ribbon 会从服务发现(如 Consul)中获取服务实例列表,并直接使用实例的 IP 地址和端口进行请求,不会依赖 DNS 解析。
- 即使服务实例的主机名无法解析,Ribbon 仍然可以通过 IP 地址直接访问服务。
WebClient
- 默认使用 Spring Cloud LoadBalancer。
- 可能会直接使用 Consul 中注册的主机名。
- 如果主机名无法解析,就会导致 DNS 解析失败。
2. 根本原因
- DNS 解析失败:
DESKTOP-5JAE1TT.mshome.net
是一个主机名,但网络环境无法解析该主机名。- 这可能是因为:
- 该主机名是本地网络中的计算机名称,无法在外部网络中解析。
- DNS 配置有问题,无法解析
.mshome.net
域名。
解决方法
1. 修改配置类
首先,我修改了 WebClient
的配置类,确保其支持负载均衡。
配置类:
1 |
|
通过这种方式,WebClient
能够正确集成 Spring Cloud LoadBalancer,支持通过服务名称解析实例地址。
2. 调试日志
为了进一步确认问题,我启用了调试日志,查看 WebClient
和负载均衡器的行为。
配置示例:
1 | logging: |
通过日志,我确认了 WebClient
是否尝试使用主机名进行请求,并验证了接下来的解决方案的有效性。
3. 强制使用 IP 地址
为了彻底解决 DNS 解析问题,我修改了 cloud-provider-payment
服务的配置,使其在 Consul 中注册时使用 IP 地址,而不是主机名。
配置示例:
1 | spring: |
这样,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 解析失败是一个常见问题。通过以下方法,我成功解决了这个问题:
- 修改配置类:确保
WebClient
支持负载均衡。 - 强制使用 IP 地址:修改服务注册配置,使用 IP 地址而不是主机名。
- 添加 DNS 解析记录:在本地
hosts
文件中添加主机名到 IP 地址的映射。 - 启用调试日志:通过日志确认问题根源和解决方案的有效性。
通过这些步骤,WebClient
的行为与 RestTemplate
一致,服务调用恢复正常。
参考文档: