某天,开发的代码在 CI 阶段遇上了这样一个看似比较「怪异」的问题:
代码在 CI 时,有一个步骤会跑去单元测试。因为依赖了远程的配置中心,所以有两份配置存在,一个在配置中心,一个是本地的 yml 文件。这两份配置里使用的是两个不同的数据库。
一般情况下,本地开发可以方便在 yml 里设置需要的属性做测试,再把不同环境下的配置加到配置中心,相当于不同的 profile。
而这个怪异问题是在跑单测阶段,在两个不同的数据库上跳来跳去,某个测试数据写在A,其它测试可能写在 B。
凭直觉就是配置加载的不对,但具体什么时候使用的是哪个配置,还不确定。而 CI 的机器是个公共的容器,没有权限登录。
我最先想到 JMX MBean 上可能会有这些信息。只要 JConsole 连接上去就能查看了,本地简单试了一下,没啥问题。
通过 org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager
这个 ObjectName,是可以拿到 Env 的对象,进而可以操作 setProperty 和 getProperty 的操作的,像这样。
在操作里可以选择 getProperty,然后把 property 名称写进去点按钮即可。
不过无奈远程机器没开放 JMX 连接,开始想其它办法。
一番折腾之后,发现机器所在平台上有个网页版的命令,支持部分 Arthas 命令,可以在页面选择命令,输入框里自己定义参数值,发送到远端服务器上执行。之前研究过一些 Arthas 的使用和实现原理(阿里监控诊断工具 Arthas 源码原理分析),感觉这或许可以派上用场。毕竟 Agent 在 Attach 到 JVM 上之后,通过 JVMTI 能干的事就太多了。
网上的文章,基本介绍都是通过 tt 的命令来实现拿到 ApplicationContext,再执行其他想要的操作。
查了下 Arthas 的文档,提供的命令里, watch 和 tt 这两个命令能拿到入参,返回值,target 等等,还支持 Ognl 表达式,不过前面说的页面平台不支持 tt,那只能通过迂回的使用 watch 来查看了。
折腾一番,给页面发送的命令加了个转义,在 watch 中通过 target 对象再执行额外的操作,终于实现了需要的功能。
通过 watch 命令执行即可,如果你需要在本地执行,那两个转义符需要去掉。
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod '{target.getApplicationContext().getEnvironment().getProperty(\"app.name\"),returnObj}' -x 2
除了拿 env 里的属性值这种功能之外,b e s f 这几个选项,能够在方法执行的不同时期进行观察,此外,也可以再增加过滤表达式,对入值增加条件,像这样:
可以灵活组合应用在分析问题的场景中。
折腾之后,能查看加载的配置文件,但具体跑单测时为什么会来回的变换配置呢? 一个同学 Debug 发现,原来是在一些测试类里加了个 @TestProperty 的注解,一些没写。这个注解重写了一个配置中心 namespace 的属性,导致拉远程配置中心配置失败,直接使用了本地的 yml 的配置,和 yml 配置和远程配置中心又不一致,所以就出现了看似怪异的问题。 :-) (:-
如果你也有需要排查的问题,可以试试 Arthas 或者相关阅读里的其它几个工具。