Docker部署Dubbo-SpingBoot假死的踩坑经历
描述
最近公司需要把一些java程序都全面转到Docker容器部署启动,而其中sys的Springboot服务启动异常的慢,就是无法显示以下启动过程耗时多长,内置Tomcat 也一直等待无法启动成功...正常情况下启动完成后出现以下情况:
2022-08-24 12:33:28.368 | INFO | o.a.c.h.Http11NioProtocol: 173 | Starting ProtocolHandler ["http-nio-10001"]
2022-08-24 12:33:28.391 | INFO | o.s.b.w.e.t.TomcatWebServer: 202 | Tomcat started on port(s): 10001 (http) with context path ''
2022-08-24 12:33:28.395 | INFO | c.x.s.SysApplication: 59 | Started SysApplication in 16.628 seconds (JVM running for 17.347)
问题分析
通过Arthas
去监控,发现是nacos
一直tw,开始就怀疑是否是容器网络之间无法互通造成的,因为之前需求POC验证时候我这边是直接nacos
和Springboot服务都在一个单独桥接网络中,这次是直接默认在docker0
的默认桥接网络上。但问题就只是在sys服务中才出现,其他的服务能快速启动,再结合nacos日志,已排查是nacos问题。
期间也怀疑过是否为服务器内核与docker版本问题,也进行了升级到centos7.9,docker也是最新版本,依旧是看运气的能启动(可能过个30分钟/或者半天即可启动完成),这种不稳定的因素其实还是有问题
诡异的是,当怀疑是否有可能是容器镜像问题时候,启动一个基础的openjdk8/java8镜像,单独把jar包丢进去运行就能正常快速
启动。
于是乎,从怀疑nacos-->服务器内核-->容器镜像,当自己也没办法时候,就看到启动一个基础镜像容器中的/etc/hosts
和我正常映射/etc/hosts
文件是有区分的
镜像容器
宿主机
于是乎,不挂载/etc/hosts
文件,卧槽...正常启动了...那证明是sys
服务中是需要获取到主机名才能正常的启动,否则需要等待一段时间才行。
后就梳理了一下sys
服务的启动逻辑
启动除了Nacos,就剩下是Dubbo了。于是乎仔细观察sys
启动过程的日志发现 hostname是"UNKNOWN"
那就测试了挂载/etc/hosts
,运行容器时,额外指定hostname名称--hostname localhost
,启动正常了。问题也就定位到是Dubbo无法获取到主机名,因为如果不指定主机名,容器中的主机名就是 容器id
,但挂载/etc/hosts
后,原来容器的文件已经被覆盖,且无法找到主机名对应的ip。因此一直等待...?
具体Dubbo代码分析
这个方法源码比较长,看起来比较费劲,不过好在这个方法注释上已经写明白 IP
地址查找顺序。
Register & bind IP address for service provider, can be configured separately. Configuration priority: environment variables -> java system properties -> host property in config file -> /etc/hosts -> default network address -> first available network address
查找顺序如图所示:
解析过程,Dubbo
将会过滤无用 IP,过滤规则如下:
下面将结合图示讲解查找顺序,只要其中一步读取 IP 符合上述规则,方法就会返回。
第一步将会调用 ServiceConfig#getValueFromConfig
从 environment variables
或 java system properties
配置 IP 地址。
这种方式通过在 JVM
启动参数中显示指定 IP 。
-DDUBBO_IP_TO_BIND=1.2.3.4
第二步通过读取 Dubbo
配置文件配置变量获取 IP。
<!-- protocol 指定整个 Dubbo 应用服务默认 IP -->
<dubbo:protocol host="1.2.3.4"/>
<!-- provider 指定 Dubbo 应用具体某个服务默认 IP -->
<dubbo:provider host="1.2.3.4"/>
第三步通过调用 InetAddress.getLocalHost().getHostAddress()
获取本地 IP。该方法将会获取机器 hostname
,然后再在 /etc/hosts
配置文件中查找 hostname
对应的配置 IP。
第四步通过 socket
连接注册中心从而获取本机 IP。
如果上述几步都不成功,Dubbo 将会轮询本机所有网卡,直到找到合适的 IP 地址。
总结
这次的问题其实不大(也就花费了3天时间折腾),就是 hosts 文件配置错误,Dubbo一直轮询都找不到hostname
对应的ip,导致卡死状态,因此在docker run
时,指定主机名即可
参考
Dubbo代码分析参考:https://www.cnblogs.com/goodAndyxublog/p/12319868.html