关于阅读开源代码的一些想法
在编写本文前,其实已经写了一篇关于Dubbo 自适应拓展特性的草稿,但是通篇写下来,所做的也不过是copy-write的过程,这样的文章对于我来说,又有什么意义呢?
尽管我多次想编写一篇详尽有效的文档,试图如同大神一样去理清Dubbo源码的脉络,但是深入其间才发现力有所不逮。因此便重新把那篇文章给删了,推倒重来,将阅读人群从别人转向自己。
这样一来,就变得清晰简单的许多。我只需要问自己以下几个问题,便能得到我想要的东西:
- 你最想了解的时Dubbo的哪个特性?
- 基于这个特性如果让你来实现,你会怎么实现?遇到哪些问题?
- 你无法解决的问题别人的解决思路是什么?
- 里面最有价值的算法或者思路是什么?
只要能解决这些问题,那么对于我来说,就是大有收获,至于别人的收获,又与我何干?
我最想了解Dubbo的哪个特性?
Dubbo的特性很多,如果一股脑的写成一篇文章,那么势必陷入僵局。围绕Dubbo一系列的特性,本文只针对Dubbo的自适应扩展与SPI部分进行解析。
那么这里顺带一提Dubbo的自适应扩展是什么?
一般情况下,Dubbo都通过SPI进行拓展,但对于有些拓展,希望在拓展方法被调用时,才灵活地根据运行时参数进行加载对应的拓展。这时候我们就称之为自适应拓展,其实就是动态拓展。
这里直接拿官网的例子来进行理解:
public interface WheelMaker {
Wheel makeWheel(URL url);
}
//WheelMaker 接口的自适应实现类如下:
public class AdaptiveWheelMaker implements WheelMaker {
public Wheel makeWheel(URL url) {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
// 1.从 URL 中获取 WheelMaker 名称
String wheelMakerName = url.getParameter("Wheel.maker");
if (wheelMakerName == null) {
throw new IllegalArgumentException("wheelMakerName == null");
}
// 2.通过 SPI 加载具体的 WheelMaker
WheelMaker wheelMaker = ExtensionLoader
.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName);
// 3.调用目标方法
return wheelMaker.makeWheel(URL url);
}
}
从上面可以看出,实际的服务实现是通过URL远程传过来的参数来动态获取的,这也就是自适应拓展。区别于直接采用SPI进行拓展加载,采用这样的方式会更加灵活。
基于这个特性如果让你来实现,你会怎么实现?遇到哪些问题?
那么解决完我想要学习什么特性的问题,第二个问题,我们姑且先不要急着去看Dubbo的实现原理,我们可以闭着眼睛,想假如是我们自己去实现,我们会怎么实现,以及遇到哪些问题?
- 1.采用什么方式去标记需要动态拓展的方法?
- 2.由于框架的开发者是不应该去关注业务以及具体业务的实现,但是又一定程度的需要将调用拦截下来,做一定的路由引导,这时候就应该要有一层简单的代理。但是由于服务的多样性以及灵活性,只能采用动态代理的方式去实现包装。那么会采用什么技术去实现动态代理?
- 假设我们已经选定了动态代理了,那么我们需要构建代理方法的相关代码,如何去构建一个完整的代理方法,分几部分,其中需要注意些什么?
当然我们并没有真正自己亲手去实现这个功能,我们不过是围绕这个特性或者这个问题,即如何动态地通过参数去适配到对应的实现方法去?(其实最low的方法就是if else),基于这个问题,我们展开了一系列的探讨。
ok,我们已经粗略地想了下这个实现过程了,乍一看,就两个点我们需要关注的:
(1)是否真的如我们所想采用了动态代理的方式?如果是,采用了哪些动态代理的技术?引申开来,当前有哪些具体的动态代理技术?他们的优缺点是什么?
(2)构建一个完整的代理方法的细节有哪些?
带着以下问题,我们开始去解剖代码。
你无法解决的问题别人的解决思路是什么?里面最有价值的算法或者思路是什么?
当然我们不可能一下子扎入到源码当中,源码是汪洋大海啊,我们得找一个路标,那就是Dubbo的官网源码导读了,它是怎么说明这个问题的解决方案的呢?
自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类
有了这个路标,我们就可以比较快捷地把握住源码的方向了。Dubbo本身提供了一系列丰富的Demo,我们可以通过已经有的Demo进行源码解读。
Demo见dubbo-common ExtensionLoader_Adaptive_Test测试类
@Test
public void test_getAdaptiveExtension_defaultAdaptiveKey() throws Exception {
{
SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension();
Map<String, String> map = new HashMap<String, String>();
URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);