一、 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诊断工具,大家在生产实践过程中如遇到问题,可以使用该利器,将会事半功倍。
微信扫描二维码,关注获取第一手技术分享
(转载本站文章请注明作者和出处 加多-技术原始积累)
Post Directory
