背景
最近准备使用阿里的容灾组件Sentinel在项目中,做一些接口的监控和限流降级功能。
本地改进Sentinel控制台,使用ZK做了持久化,调试没有问题后,准备测试并上线到生产环境。此时出现一个比较怪的问题。
问题现象
本地启动客户端项目,Sentinel控制台可以正常显示监控信息,发布到Linux测试环境和 线上环境,控制台不显示应用。
定位问题
第一步,首先怀疑是网络问题,因为Linux 服务器部署的 控制台 和 客户端不在同一台机器上。
telnet ip port
结果:可以通
netstat 查看端口和网络情况,均正常。
第二步,网络是通的,此时怀疑是否是防火墙、权限等拦截了。
找运维沟通,确定测试环境是内网,没有类似的权限限制,检查了防火墙,也没有拦截相应的端口。
没办法,只能去github 查看Sentinel 的 wiki.
根据wiki的FAQ排查问题
其他配置既然本地可以,说明代码和配置没问题,看到wiki 说 fastjson 版本不一致,可能导致客户端 埋点数据上报 控制台不显示。
统一fastjson 为最新版1.2.62 后,重新编译发布测试环境,依然不可以。。。。
又检查了几遍测试配置的ip 和 hostname 映射,均没问题。
此时有些犯难了,能想到都已经检查过了,为什么本地和测试环境会有区别?
难道是 JDK版本不一致导致? 本地 jdk1.8.0_191, 测试环境jdk1.8.0_131。
没办法了,开始排除法,剔除一切不同的环境因素。统一 jdk版本后,发现依然存在该问题。。。。吐血
第三步,深入分析JVM线程和Sentinel 启动日志 和 埋点发送日志
查看了Sentinel 自身的record.log , 没有 任何error 日志。启动也没有报错。
最后只能通过profiling 手段来在线分析。
以下是我截取的本地和Linux环境 的 运行时 Threads.
Linux环境:
本地:
终于发现了一些问题,sentinel 相关的Threads, Linux 服务器比本地少了一些。
少的这些线程作用是啥? 为什么这些线程没有启动?
到了这里,没办法了,只能去看源码,了解Sentinel 源码运行情况
第四步, 阅读源码
查看源码发现这几个线程在 sentinel 客户端初始化的时候,就会启动,那说明Sentiel 没有初始化成功,可是不成功为什么没有报错信息呢?
这里感觉有点坑。。。
初始化代码如下:
发现启动过程中有一些日志
去查找日志:
本地启动日志:
Linux上的日志:
锁定了问题,日志区别就是红框内部分。
CommandCenterInitFunc 和 HeartbeatSenderInitFunc 、SimpleHttpCommandCenter 没有打印,没有启动。
到这里,问题基本浮出水面了。
源码中可以看到,这里使用了 ServiceLoader 加载接口实现。 这里使用了SPI 技术。
ServiceLoader 加载SPI 接口实现类时,需要将接口实现配置到 META-INF/services/ 下,解压Linux服务器上的jar 包,进入该目录。发现 InitFunc 接口配置文件,只有 com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit。
其他的SPI实现类均丢失了。
问题终于定位。。。。剩下的就是找出解决办法。
问题解决
查找资料,了解到maven编译问题,会导致编译SPI接口出现覆盖,使用shade 插件可以解决。
在 pom.xml 中增加以下:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
重新打包编译,解压jar 后查看META-INF/services/ 下的InitFunc 配置:
发现,其他几个类已经有了。
长呼一口气。。。。
发布到测试环境,启动后,展示正常:
总结
该问题定位话费了不少时间,过后反思。
1. 本地为何可以?
因为本地 IDEA 启动是直接编译执行,不存在 SPI 丢包情况, 而Linux 其实是启动jar 包运行。
启动方式不一样,这里是个误区。