arthas助力阅读dubbo源码

2020-09-10  本文已影响0人  王兴岭

框架

Dubbo 2.7.7
arthas-boot 3.4.0
系统: Mac

1. Linux/Unix/Mac 安装arthas

curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

SPI是dubbo实现动态修改服务参数的基石,比如下面代码

    RegistryFactory REGISTER_FACTORY = ExtensionLoader.getExtensionLoader(RegistryFactory.class)
        .getAdaptiveExtension();
    Registry registry = REGISTER_FACTORY
        .getRegistry(URL.valueOf("zookeeper://127.0.0.1:2181?client=curator"));

返回的REGISTER_FACTORYorg.apache.dubbo.registry.RegistryFactory$Adaptive@19bb07ed,
但是这个以$Adaptive为结尾的RegistryFactory到底是什么鬼,整个项目查找都没有这个class文件,如果对Dubbo的SPI源码了解的话应该就知道RegistryFactory$Adaptive是动态拼接的字符串,然后使用字节码技术编译成class,并通过自定的ClassLoader加载到java虚拟机内存中的,所以dubbo框架源码中是找不到这个class的.但是这个class的内容我很好奇到底是什么?

方法1:使用arthas(本文的重点)(包含完整的步骤),以AdapteExtendsionTestDemo为例(要使用debug模式启动main方法,不然arthas连接AdapteExtendsionTest进程失败)

public class AdapteExtendsionTest {
    RegistryFactory REGISTER_FACTORY = ExtensionLoader.getExtensionLoader(RegistryFactory.class)
        .getAdaptiveExtension();
    Registry registry = REGISTER_FACTORY
        .getRegistry(URL.valueOf("zookeeper://127.0.0.1:2181?client=curator"));
    System.in.read();
  }
}

最后要有System.in.read(),获取其他方法,让main线程保活,不然没办法使用arthas,通过断点发现REGISTER_FACTORY.toString()返回的是org.apache.dubbo.registry.RegistryFactory$Adaptive@19bb07ed,把@19bb07ed去掉就是RegistryFactory$Adaptive完整的类路径org.apache.dubbo.registry.RegistryFactory$Adaptive.
打开终端启动arthas-boot服务

 java -jar arthas-boot.jar

执行结果如下

[INFO] arthas-boot version: 3.4.0
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 24835 
  [2]: 30548 org.jetbrains.jps.cmdline.Launcher
  [3]: 30549 com.leimo.demo.AdapteExtendsionTest
  [4]: 25244 org.jetbrains.idea.maven.server.RemoteMavenServer36
  [5]: 508 org.apache.zookeeper.server.quorum.QuorumPeerMain

通过上面的列表可以找到AdapteExtendsionTest,就是我们刚刚跑的测试类名
[3]: 30549 com.leimo.demo.AdapteExtendsionTest
编号是[3],在控制台输入3

3
[INFO] arthas home: /Users/lemo-wu/.arthas/lib/3.4.0/arthas
[INFO] Try to attach process 31331
[INFO] Attach process 31331 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.                           
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'                          
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.                          
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |                         
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'                          
                                                                                

wiki      https://arthas.aliyun.com/doc                                         
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html                   
version   3.4.0                                                                 
pid       31331                                                                 
time      2020-09-10 14:49:41                                                   

[arthas@31331]$ 

如果显示[arthas@xxxx]$就表示连接成功了
然后在arhhas终端输入

[arthas@31331]$ jad org.apache.dubbo.registry.RegistryFactory$Adaptive

控制台就会将class反编译的结果打印在控制台上了

[arthas@31331]$ jad org.apache.dubbo.registry.RegistryFactory$Adaptive

ClassLoader:                                                                                                                                                                                                             
+-sun.misc.Launcher$AppClassLoader@18b4aac2                                                                                                                                                                              
  +-sun.misc.Launcher$ExtClassLoader@56ac3a89                                                                                                                                                                            

Location:                                                                                                                                                                                                                
/Users/lemo-wu/.m2/repository/org/apache/dubbo/dubbo/2.7.7/dubbo-2.7.7.jar                                                                                                                                               

/*
 * Decompiled with CFR.
 */
package org.apache.dubbo.registry;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryFactory;

public class RegistryFactory$Adaptive
implements RegistryFactory {
    @Override
    public Registry getRegistry(URL uRL) {
        String string;
        if (uRL == null) {
            throw new IllegalArgumentException("url == null");
        }
        URL uRL2 = uRL;
        String string2 = string = uRL2.getProtocol() == null ? "dubbo" : uRL2.getProtocol();
        if (string == null) {
            throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.registry.RegistryFactory) name from url (").append(uRL2.toString()).append(") use keys([protocol])").toString());
        }
        RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(string);
        return registryFactory.getRegistry(uRL);
    }
}

Affect(row-cnt:1) cost in 563 ms.

jad是arthas的命令集之一,作用就是用来将class反编译成java输出.如果想将反编译的内容输出到一个文件中,可以使用 > 输入文件路径,比如

[arthas@31331]$ jad org.apache.dubbo.registry.RegistryFactory$Adaptive > /opt/abc.java

验证输出结果

# cd /opt
# ls
# cat abc.java

控制台输出结果如下

ClassLoader:
+-sun.misc.Launcher$AppClassLoader@18b4aac2
  +-sun.misc.Launcher$ExtClassLoader@56ac3a89

Location:
/Users/lemo-wu/.m2/repository/org/apache/dubbo/dubbo/2.7.7/dubbo-2.7.7.jar

/*
 * Decompiled with CFR.
 */
package org.apache.dubbo.registry;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryFactory;

public class RegistryFactory$Adaptive
implements RegistryFactory {
    @Override
    public Registry getRegistry(URL uRL) {
        String string;
        if (uRL == null) {
            throw new IllegalArgumentException("url == null");
        }
        URL uRL2 = uRL;
        String string2 = string = uRL2.getProtocol() == null ? "dubbo" : uRL2.getProtocol();
        if (string == null) {
            throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.registry.RegistryFactory) name from url (").append(uRL2.toString()).append(") use keys([protocol])").toString());
        }
        RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(string);
        return registryFactory.getRegistry(uRL);
    }
}

Affect(row-cnt:1) cost in 202 ms.

方法2. 找到到生成java字符串的方法

org.apache.dubbo.common.extension.ExtensionLoader类的createAdaptiveExtensionClass方法

  private Class<?> createAdaptiveExtensionClass() {
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

通过IDE在ClassLoader classLoader = findClassLoader();断点,然后watch code,将code的内容粘贴到一个文本编辑区中查看

package org.apache.dubbo.registry;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryFactory;

public class RegistryFactory$Adaptive
implements RegistryFactory {
    @Override
    public Registry getRegistry(URL uRL) {
        String string;
        if (uRL == null) {
            throw new IllegalArgumentException("url == null");
        }
        URL uRL2 = uRL;
        String string2 = string = uRL2.getProtocol() == null ? "dubbo" : uRL2.getProtocol();
        if (string == null) {
            throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.registry.RegistryFactory) name from url (").append(uRL2.toString()).append(") use keys([protocol])").toString());
        }
        RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(string);
        return registryFactory.getRegistry(uRL);
    }
}

参考资料:
https://arthas.aliyun.com/doc/en/quick-start.html

上一篇 下一篇

猜你喜欢

热点阅读