一、 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診斷工具,大家在生産實踐過程中如遇到問題,可以使用該利器,将會事半功倍。