背景
- 系统升级改造,当前已有网关A,为了平滑升级,先部署网关B,并且在网关A和B前加一层转发层(IAS)。
定时任务调用网关B下的服务接口时,接口返回:
{ "timestamp":"2022-05-09T10:02:35.889+08:00", "path":"/xxx/yyy", "status":404, "error":"Not Found", "message":null, "requestId":"06f3daf5-67392626" }
- 转发层(IAS)的请求日志没有查到请求记录,网关A的请求日志也没有记录。
问题定位
- 确认IAS和网关B是否正常:不走域名,通过IP请求,接口调用成功。结论:正常。
- 确认DNS是否正常:登陆容器,在容器内执行curl,接口调用成功。结论:正常。
综上:初步怀疑是调用服务的程序有问题。服务采用spring-webflux
的WebClient
调用接口,需要排查WebClient
是否有DNS缓存。
问题确认
通过谷歌关键字搜索,成功定位到一个github问题:Document How To Switch DNS Resolver · Issue #1431 · reactor/reactor-netty · GitHub,再通过问题里的链接,定位到官网Reactor Netty Reference Guide (projectreactor.io),通过官方文档的说明,得知webclient
底层用到的reactor-netty
有对DNS做了缓存,默认最长缓存时间为Int的最大值,也就是21亿秒
。
如果是缓存原因,那重启之后服务调用应该就会正常了,尝试重启之后发现确实可以调用到服务了。
问题解决
修改DNS缓存时长
根据文档,可以设置DNS缓存时间
webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create().followRedirect(true)
))
.build();
改为以下即可
webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create()
.resolver(spec -> spec.queryTimeout(Duration.ofSeconds(dnsCacheMaxTimeToLive)))
.followRedirect(true)
))
.build();
但是当前项目springboot版本是2.1.3.RELEASE
,对应默认引入的reactor-netty
版本是0.8.5.RELEASE
,该版本没有resolver
方法。
升级版本
显式引入
reactor-netty
,指定版本1.0.15
,启动报错NoSuchMethodException,发现是显式引入的reactor-netty
跟springboot默认引入的netty-codec-http
版本冲突了。- 升级springboot版本到
2.3.0.RELEASE
. - 顺便升级spring版本到5.3.18,解决下spring零日漏洞问题。由于springboot默认有引入spring,只需修改默认引入的版本即可:配置maven的
properties
:<spring-framework.version>5.3.18</spring-framework.version>
,解决下spring零日漏洞问题。 - 由于
spring-boot-dependencies
默认有配置了netty.version
,同上配置maven的properties
:<netty.version>4.1.72.Final</netty.version>
,覆盖springboot的默认版本。 - 引入
io.projectreactor:reactor-core
,版本3.4.17
。
- 升级springboot版本到
执行完以上升级之后,系统启动成功,但是调外部的一个接口时报错:
DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144
。新版springboot默认限制数据包大小为256K,参照以下文章spring-cloud-gateway缓存区不够用的解决办法来解决,由于我这是WebClient
请求外部接口报错,所以创建WebClient
时调大限制:webClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector( HttpClient.create() .resolver(spec -> spec.queryTimeout(Duration.ofSeconds(dnsCacheMaxTimeToLive))) .followRedirect(true) )) .codecs(configurer -> configurer .defaultCodecs() .maxInMemorySize(maxInMemorySize)) .build();
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。