arthas定位dubbo手动注册eureka异常

785 阅读5分钟

文章内容篇幅有限,主要是想为 arthas 做贡献,有些细节不在文中展开。如有兴趣可进一步交流。

很久没有写技术分享博客,因为发现一个好的工具确实有点忍不住分享一下,毕竟独乐乐不如众乎乎。这里需要说的主角就是artahs。arthas 使用文档很详细,我这里主要记录一下使用 arthas 的一点总结。

1 使用背景

在一个大的团队里面会因为很多历史原因或客观因素导致技术栈并不统一,我们就遇到这么一个问题。老项目是使用 dubbo 框架的 dubbo 协议进行服务交互,有新的项目是使用 springcloud 体系的 feignclient 框架的 http 协议进行交互。那么就需要解决两个问题。

  • 问题 1 是 dubbo 服务需要支持 dubbo 协议又需要支撑 http 协议
  • 问题 2 是 feignclient 框架能够无损调用 dubbo 服务的 http 协议(无损指的是 dubbo 对 feignclient 调用方能够做到服务动态感知,负载均衡不需要做二次开发)

2 解决思路

有了以上的目标,我们就需要想办法解决问题。解决第一个问题倒是比较容易,dubbo 本身就提供的 http 协议暴漏。也就是将老工程 dubbo 升级、配置两种协议问题一就这么解决了。但是针对问题 2,我们可以罗列一下遇到的问题。dubbo 注册使用的是 zk 注册中心,springcloud 工程使用 eureka 注册中心。这里顺带也要赞美一下 feignclient 框架,feignclient 接入服务的门槛很低这样兼容了很多语言、环境等带来的差异,也就是只要是 http 协议的接口 feignclient 就能调用。到这里实际上通过 feignclient 直接调用 dubbo 服务暴漏的 http 协议接口是能够走的通,只不过没法做到负载均衡,失败转移等能力。说了这么多总结一下吧。

  • 现在通过 feignclient 直连 dubbo 框架工程的 http 协议能够正常执行
  • 分布式环境下需要解决直连的弊端(无法负载均衡,无法失败转移,无法动态扩容等等) 好在通过分析了 eureka 源码以后打开了另一个大门,eureka 实际上是独立的组件,而且提供手动注册服务的能力(即使没有修改源码就有了)。现在解决思路就是在 dubbo 工程里面引入 eureka 组件,手动将服务注册到 eureak 以便 feignclient 能够无损调用。

3 主角(arthas)登场

为了将 dubbo 框架工程提供注册 eureka 的能力,并且能够做到优雅上线和下线。我们主要是借助了 dubbo 的 spi 扩展能力中的 container 扩展。代码如下:

class EurekaDubboContainer implements Container {...}

有了以上代码还需要做一件重要的事情,就是声明 container 的全路径。 META-INF/dubbo/org.apache.dubbo.container.Container: xxx=com.xxx.XxxContainer。 当时我们配置这里的代码spring=com.xxx.EurekaDubboContainer。 当所有准备好了以后我们启动工程,发现无异常输出,进程完美。但是 feignclient 怎么也无法调用。最主要是启动过程中没有任何异常输出,大经过大量论证以后,就在快绝望的时候,我发现了 arthas,arthas 可以查看内存中对象属性值以及执行对象的方法,我欣喜若狂。通过之前对 dubbo 注册过程源码分析:

com.alibaba.dubbo.common.extension.ExtensionLoader#loadFile

} catch (Throwable t) {
	IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
	exceptions.put(line, e);
}

在 dubbo 启动过程会从 jar 包中扫描配置的 META-INF 中配置的 container,在加载的时候这个异常是直接存放在了 loader 类的域中,我猜测可能是为了解决 container 隔离所以异常并没有抛出。当前我主要目标还是分析为啥我定义的扩展容易没有启动。部署 arthas 以后我开始了分析之路。

  • sc -d *.EurekaDubboContainer 发现类已经正常加载,说明有被 load 加载。
  • jad *.EurekaDubboContainer 发现加载的类代码也是在正常(排除包不对的可能)
  • ognl '#loader=@com.alibaba.dubbo.container.Main@loader,#loader.cachedInstances' 这里发现了问题,如果是正常被 load 的 container 会被存在到 ExtensionLoader 的 cachedInstances 域中(默认的spring,log4j存在),但是我自定义的container竟然没找到
  • ognl '#loader=@com.alibaba.dubbo.container.Main@loader,#loader.exceptions' 这里发现了之所有没有加载成功的原因,在 exceptions 中有声明。 ognl查看结果截图 通过以上分析,问题非常明显了,在META-INF中指定的key重复了。还是没深入理解dubbo中的spi文档上的‘xxx’是自定义的意思。到这里修改key以后一切按照计划执行。

4 结束

通过一波操作,我们发现从技术角度其实没有解决不了的方法,只是需要多想一想,多想想办法其实总可以找到方法解决。包括使用arthas上ognl如何查看load实例中的非静态域,直接获取是无法获取的,因为没有存在在arthas上下文中,所以变通一下思路通过Main的静态域获取实例,在通过实例变量获取非静态域的值。技术没有终止,愿你我一同进步。为开源贡献微薄的力量。若对细节有兴趣的朋友,可留言交流。