基于Arthas探究Apache Dubbo技术内幕

一、 Arthas的介绍与安装

Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱,当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  • 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?

  • 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

  • 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

  • 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

  • 是否有一个全局视角来查看系统的运行状况?

  • 有什么办法可以监控到JVM的实时运行状态?

Arthas的github地址为:https://github.com/alibaba/arthas

安装Arthas第一步需要下载arthas-boot.jar:

wget https://alibaba.github.io/arthas/arthas-boot.jar

第二步启动:

java -jar arthas-boot.jar

然后会让列出所有Java进程,让你选择进入那个进程,选择1就会进入我们的服务提供者进程:

admindeMacBook-Pro :: ~ » java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.1.0
[INFO] Found existing java process, please choose one and hit RETURN.
* [1]: 1224 com.books.dubbo.demo.provider.ApiProvider
  [2]: 1192  
  [3]: 1134 org.apache.zookeeper.server.quorum.QuorumPeerMain

选择1后就会关联到进程1224:

[INFO] arthas home: /Users/luxu.zlx/.arthas/lib/3.1.0/arthas
[INFO] Try to attach process 1224
[INFO] Attach process 1224 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'
wiki: https://alibaba.github.io/arthas
version: 3.1.0
pid: 1224
time: 2019-05-06 11:23:04
$

二、查看扩展接口适配器类的源码

在《Dubbo框架内核原理剖析》我们讲解了Dubbo的适配器原理,知道每个扩展接口对应一个适配器类,并且这个适配器类是使用动态编译技术生成的,正常情况下除非我们使用Debug才能看到适配器的源码,但是使用arthas我们就可以在服务启动的情况下查看某个适配器源码。

比如我们想看扩展接口org.apache.dubbo.rpc.Protocol的适配器类源码,在可以在启动dubbo服务和arthas后,在arthas的控制台执行下面命令:

$ jad org.apache.dubbo.rpc.Protocol$Adaptive
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@4e25154f
  +-sun.misc.Launcher$ExtClassLoader@68837a77
Location:
/Users/luxu.zlx/.m2/repository/org/apache/dubbo/dubbo/2.7.1/dubbo-2.7.1.jar
/*
 * Decompiled with CFR 0_132.
 */
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.RpcException;
public class Protocol$Adaptive
implements Protocol {
    @Override
    public void destroy() {
        throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
    @Override
    public int getDefaultPort() {
        throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
    public Invoker refer(Class class_, URL uRL) throws RpcException {
        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.rpc.Protocol) name from url (").append(uRL2.toString()).append(") use keys([protocol])").toString());
        }
        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(string);
        return protocol.refer(class_, uRL);
    }
    public Exporter export(Invoker invoker) throws RpcException {
        String string;
        if (invoker == null) {
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        }
        if (invoker.getUrl() == null) {
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        }
        URL uRL = invoker.getUrl();
        String string2 = string = uRL.getProtocol() == null ? "dubbo" : uRL.getProtocol();
        if (string == null) {
            throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (").append(uRL.toString()).append(") use keys([protocol])").toString());
        }
        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(string);
        return protocol.export(invoker);
    }
}
Affect(row-cnt:1) cost in 618 ms.
$

同理其他扩展接口的适配器类源码可以使用类似方法得到。

三、 查看服务提供端Wrapper类的源码

在《Dubbo使用JavaAssist减少反射调用开销》一节我们讲到Dubbo会给每个服务提供者的实现生产一个Wrapper类,这个wrapper类里面最终调用服务提供者的接口实现类,wrapper类的存在是为了减少反射的调用。那么我们可以使用jad命令方便的查看某一个服务实现类被wrapper后的类,以便探其实如何工作的。

可以在启动dubbo服务提供端和arthas后,在arthas的控制台执行下面命令:

$ jad org.apache.dubbo.common.bytecode.Wrapper1
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@4e25154f
  +-sun.misc.Launcher$ExtClassLoader@68837a77
Location:
/Users/luxu.zlx/.m2/repository/org/apache/dubbo/dubbo/2.7.1/dubbo-2.7.1.jar
/*
 * Decompiled with CFR 0_132.
 *
 * Could not load the following classes:
 *  com.books.dubbo.demo.api.PoJo
 *  com.books.dubbo.demo.api.Result
 *  com.books.dubbo.demo.provider.GreetingServiceImpl
 */
package org.apache.dubbo.common.bytecode;
import com.books.dubbo.demo.api.PoJo;
import com.books.dubbo.demo.api.Result;
import com.books.dubbo.demo.provider.GreetingServiceImpl;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.apache.dubbo.common.bytecode.ClassGenerator;
import org.apache.dubbo.common.bytecode.NoSuchMethodException;
import org.apache.dubbo.common.bytecode.NoSuchPropertyException;
import org.apache.dubbo.common.bytecode.Wrapper;
public class Wrapper1
extends Wrapper
implements ClassGenerator.DC {
    public static String[] pns;
    public static Map pts;
    public static String[] mns;
    public static String[] dmns;
    public static Class[] mts0;
    public static Class[] mts1;
    @Override
    public String[] getMethodNames() {
        return mns;
    }
    @Override
    public String[] getDeclaredMethodNames() {
        return dmns;
    }
    @Override
    public String[] getPropertyNames() {
        return pns;
    }
    @Override
    public boolean hasProperty(String string) {
        return pts.containsKey(string);
    }
    @Override
    public Object getPropertyValue(Object object, String string) {
        try {
            GreetingServiceImpl greetingServiceImpl = (GreetingServiceImpl)object;
        }
        catch (Throwable throwable) {
            throw new IllegalArgumentException(throwable);
        }
        throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" field or setter method in class com.books.dubbo.demo.provider.GreetingServiceImpl.").toString());
    }
    @Override
    public void setPropertyValue(Object object, String string, Object object2) {
        try {
            GreetingServiceImpl greetingServiceImpl = (GreetingServiceImpl)object;
        }
        catch (Throwable throwable) {
            throw new IllegalArgumentException(throwable);
        }
        throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" field or setter method in class com.books.dubbo.demo.provider.GreetingServiceImpl.").toString());
    }
    public Object invokeMethod(Object object, String string, Class[] arrclass, Object[] arrobject) throws InvocationTargetException {
        GreetingServiceImpl greetingServiceImpl;
        try {
            greetingServiceImpl = (GreetingServiceImpl)object;
        }
        catch (Throwable throwable) {
            throw new IllegalArgumentException(throwable);
        }
        try {
            if ("sayHello".equals(string) && arrclass.length == 1) {
                return greetingServiceImpl.sayHello((String)arrobject[0]);
            }
            if ("testGeneric".equals(string) && arrclass.length == 1) {
                return greetingServiceImpl.testGeneric((PoJo)arrobject[0]);
            }
        }
        catch (Throwable throwable) {
            throw new InvocationTargetException(throwable);
        }
        throw new NoSuchMethodException(new StringBuffer().append("Not found method \"").append(string).append("\" in class com.books.dubbo.demo.provider.GreetingServiceImpl.").toString());
    }
    public Class getPropertyType(String string) {
        return (Class)pts.get(string);
    }
}
Affect(row-cnt:1) cost in 230 ms.
$

如上可知找到GreetingServiceImpl服务类被wrapper后的代码。

四、 查询Dubbo启动后都有哪些Filter

在Dubbo中Filter链是一个亮点,通过Filter链可以对服务请求和服务处理流程进行干预,有时候我们想要知道运行时到底有哪些Filter在工作,这时候使用arthas的trace命令显得比较重要。

可以在启动dubbo服务提供端和arthas后,在arthas的控制台执行下面命令:

$ trace org.apache.dubbo.rpc.Filter *
`---ts=2019-05-06 11:45:14;thread_name=DubboServerHandler-192.168.0.109:20880-thread-14;id=39;is_daemon=true;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@4e25154f
    `---[1006.264664ms] org.apache.dubbo.rpc.filter.EchoFilter:invoke()
        +---[0.014367ms] org.apache.dubbo.rpc.Invocation:getMethodName()
        +---[0.011227ms] java.lang.String:equals()
        `---[1006.148807ms] org.apache.dubbo.rpc.Invoker:invoke()
            `---[1006.056904ms] org.apache.dubbo.rpc.filter.ClassLoaderFilter:invoke()
                +---[min=0.00241ms,max=0.316038ms,total=0.331369ms,count=3] java.lang.Thread:currentThread()
                +---[0.010192ms] java.lang.Thread:getContextClassLoader()
                +---[0.034606ms] org.apache.dubbo.rpc.Invoker:getInterface()
                +---[0.002414ms] java.lang.Class:getClassLoader()
                +---[min=0.002935ms,max=0.006914ms,total=0.009849ms,count=2] java.lang.Thread:setContextClassLoader()
                `---[1005.10032ms] org.apache.dubbo.rpc.Invoker:invoke()
                    `---[1004.957999ms] org.apache.dubbo.rpc.filter.GenericFilter:invoke()
                        +---[0.018287ms] org.apache.dubbo.rpc.Invocation:getMethodName()
                        +---[0.013182ms] java.lang.String:equals()
                        `---[1004.81856ms] org.apache.dubbo.rpc.Invoker:invoke()
                            +---[1004.476265ms] org.apache.dubbo.rpc.filter.ContextFilter:invoke()
                            |   +---[0.014525ms] org.apache.dubbo.rpc.Invocation:getAttachments()
                            |   +---[0.03847ms] java.util.HashMap:<init>()
                            |   +---[min=0.001196ms,max=0.043312ms,total=0.087213ms,count=10] java.util.Map:remove()
                            |   +---[min=0.001419ms,max=0.012589ms,total=0.019495ms,count=3] org.apache.dubbo.rpc.RpcContext:getContext()
                            |   +---[0.008891ms] org.apache.dubbo.rpc.RpcContext:setInvoker()
                            |   +---[0.039752ms] org.apache.dubbo.rpc.RpcContext:setInvocation()
                            |   +---[min=9.47E-4ms,max=0.010978ms,total=0.011925ms,count=2] org.apache.dubbo.rpc.Invoker:getUrl()
                            |   +---[0.007281ms] org.apache.dubbo.common.URL:getHost()
                            |   +---[0.004561ms] org.apache.dubbo.common.URL:getPort()
                            |   +---[0.008838ms] org.apache.dubbo.rpc.RpcContext:setLocalAddress()
                            |   +---[min=7.99E-4ms,max=0.005502ms,total=0.006301ms,count=2] org.apache.dubbo.rpc.RpcContext:getAttachments()
                            |   +---[0.009082ms] java.util.Map:putAll()
                            |   +---[0.002867ms] org.apache.dubbo.rpc.RpcInvocation:setInvoker()
                            |   +---[1003.89518ms] org.apache.dubbo.rpc.Invoker:invoke()
                            |   |   `---[1003.31997ms] org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter:invoke()
                            |   |       +---[min=0.003088ms,max=0.00877ms,total=0.011858ms,count=2] java.lang.System:currentTimeMillis()
                            |   |       +---[1003.209055ms] org.apache.dubbo.rpc.Invoker:invoke()
                            |   |       |   +---[1002.79439ms] org.apache.dubbo.rpc.filter.TimeoutFilter:invoke()
                            |   |       |   |   +---[min=0.001023ms,max=0.007899ms,total=0.008922ms,count=2] org.apache.dubbo.rpc.Invocation:getAttachments()
                            |   |       |   |   +---[0.003378ms] java.lang.System:currentTimeMillis()
                            |   |       |   |   +---[0.008515ms] java.lang.String:valueOf()
                            |   |       |   |   +---[0.00812ms] java.util.Map:put()
                            |   |       |   |   `---[1002.161502ms] org.apache.dubbo.rpc.Invoker:invoke()
                            |   |       |   |       `---[1002.09319ms] org.apache.dubbo.monitor.support.MonitorFilter:invoke()
                            |   |       |   |           +---[0.007223ms] org.apache.dubbo.rpc.Invoker:getUrl()
                            |   |       |   |           +---[0.009356ms] org.apache.dubbo.common.URL:hasParameter()
                            |   |       |   |           `---[1002.006852ms] org.apache.dubbo.rpc.Invoker:invoke()
                            |   |       |   |               +---[1001.720803ms] org.apache.dubbo.rpc.filter.ExceptionFilter:invoke()
                            |   |       |   |               |   `---[1001.614361ms] org.apache.dubbo.rpc.Invoker:invoke()
                            |   |       |   |               `---[0.100831ms] org.apache.dubbo.rpc.filter.ExceptionFilter:onResponse()
                            |   |       |   |                   `---[0.021178ms] org.apache.dubbo.rpc.Result:hasException()
                            |   |       |   `---[0.270409ms] org.apache.dubbo.rpc.filter.TimeoutFilter:onResponse()
                            |   |       |       +---[0.01415ms] org.apache.dubbo.rpc.Invocation:getAttachment()
                            |   |       |       +---[0.003677ms] java.lang.System:currentTimeMillis()
                            |   |       |       +---[0.05753ms] java.lang.Long:valueOf()
                            |   |       |       +---[0.00897ms] java.lang.Long:longValue()
                            |   |       |       +---[min=0.002242ms,max=0.008982ms,total=0.011224ms,count=2] org.apache.dubbo.rpc.Invoker:getUrl()
                            |   |       |       +---[0.006836ms] org.apache.dubbo.rpc.Invocation:getMethodName()
                            |   |       |       `---[0.019452ms] org.apache.dubbo.common.URL:getMethodParameter()
                            |   |       `---[0.013511ms] java.util.concurrent.ConcurrentMap:size()
                            |   +---[0.018903ms] org.apache.dubbo.rpc.RpcContext:removeContext()
                            |   `---[0.009552ms] org.apache.dubbo.rpc.RpcContext:removeServerContext()
                            `---[0.168211ms] org.apache.dubbo.rpc.filter.ContextFilter:onResponse()
                                +---[0.016141ms] org.apache.dubbo.rpc.RpcContext:getServerContext()
                                +---[0.002753ms] org.apache.dubbo.rpc.RpcContext:getAttachments()
                                `---[0.010771ms] org.apache.dubbo.rpc.Result:addAttachments()

可知当前运行的服务提供端Filter链中Filter有:

org.apache.dubbo.rpc.filter.EchoFilter
org.apache.dubbo.rpc.filter.ClassLoaderFilter
org.apache.dubbo.rpc.filter.GenericFilter
org.apache.dubbo.rpc.filter.ContextFilter
org.apache.dubbo.rpc.filter.TimeoutFilter
org.apache.dubbo.rpc.filter.ExceptionFilter

同理大家可以在服务消费端启动后看其运行都有哪些Filter。

五、 总结

Arthas 是Alibaba开源的Java诊断工具,大家在生产实践过程中如遇到问题,可以使用该利器,将会事半功倍。

最后作者的Apache Dubbo核心技术内幕 一书已经出版,对Dubbo内核原理感兴趣的通信可以单击下发购买。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值