詳解Alibaba?Java診斷工具Arthas查看Dubbo動態(tài)代理類
閱讀Dubbo源碼過程中,會發(fā)現(xiàn),Dubbo消費(fèi)端在做遠(yuǎn)程調(diào)用時,默認(rèn)通過 Javassist 框架為服務(wù)接口生成動態(tài)代理類,調(diào)用javassist框架下的JavassistProxyFactory類的getProxy(Invoker invoker, Class<?>[] interfaces)方法,動態(tài)生成一個存放在JVM中的動態(tài)代理類。
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}那么,問題來了,如果我們想要一睹該動態(tài)生成的代理類內(nèi)部結(jié)構(gòu)是怎樣的,如何才能便捷做到的?
這就是我想介紹的一款工具,它可以幫助我們查看JDK或者javassist生成的動態(tài)代理類,當(dāng)然,它的功能遠(yuǎn)不止此,還可以在生產(chǎn)環(huán)境進(jìn)行診斷。
Arthas 是Alibaba開源的Java診斷工具,官方在線文檔地址:https://arthas.aliyun.com/doc/
根據(jù)官網(wǎng)上的介紹,它還可以解決以下問題————
當(dāng)你遇到以下類似問題而束手無策時,Arthas可以幫助你解決:
這個類從哪個 jar 包加載的?為什么會報各種類相關(guān)的 Exception?
我改的代碼為什么沒有執(zhí)行到?難道是我沒 commit?分支搞錯了?
遇到問題無法在線上 debug,難道只能通過加日志再重新發(fā)布嗎?
線上遇到某個用戶的數(shù)據(jù)處理有問題,但線上同樣無法 debug,線下無法重現(xiàn)!
是否有一個全局視角來查看系統(tǒng)的運(yùn)行狀況?
有什么辦法可以監(jiān)控到JVM的實時運(yùn)行狀態(tài)?
怎么快速定位應(yīng)用的熱點(diǎn),生成火焰圖?
怎樣直接從JVM內(nèi)查找某個類的實例?
這些方案本文暫不展開,這里只展開通過該工具查看Dubbo生成的動態(tài)代理類。
我是直接在使用dubbo-parent源碼中的例子,分別啟動了提供者與消費(fèi)者。

首先,啟動提供者方法——
public class Application {
public static void main(String[] args) throws Exception {
startWithBootstrap();
}
private static boolean isClassic(String[] args) {
return args.length > 0 && "classic".equalsIgnoreCase(args[0]);
}
private static void startWithBootstrap() {
ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
service.setInterface(DemoService.class);
service.setRef(new DemoServiceImpl());
DubboBootstrap bootstrap = DubboBootstrap.getInstance();
RegistryConfig registryConfig = new RegistryConfig("zookeeper://127.0.0.1:2181");
registryConfig.setTimeout(20000);
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName("dubbo");
protocolConfig.setHost("192.168.100.1");
protocolConfig.setPort(20877);
bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))
.registry(registryConfig)
.service(service)
.protocol(protocolConfig)
.start()
.await();
}
}
注意,需要配置RegistryConfig自己的zookeeper, protocolConfig.setHost("xxx.xxx.xxx.xxx")設(shè)置成你本地內(nèi)網(wǎng)的ip即可;
DemoServiceImpl類詳情——
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sayHello(String name) {
logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
}
public CompletableFuture<String> sayHelloAsync(String name) {
return null;
}
接著,啟動消費(fèi)者,這里可以設(shè)置一個休眠時間,這樣就可以一直維持消費(fèi)者運(yùn)行在內(nèi)存當(dāng)中——
public class Application {
public static void main(String[] args) {
runWithRefer();
}
private static void runWithRefer() {
RegistryConfig registryConfig = new RegistryConfig("zookeeper://127.0.0.1:2181");
registryConfig.setTimeout(30000);
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName("dubbo");
protocolConfig.setHost("192.168.200.1");
protocolConfig.setPort(20899);
ReferenceConfig<DemoService> reference = new ReferenceConfig<>();
reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer"));
reference.setRegistry(registryConfig);
reference.setInterface(DemoService.class);
DemoService service = reference.get();
String message = service.sayHello("dubbo");
System.out.println("打印了5555555"+message);
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
當(dāng)Dubbo的服務(wù)提供者與消費(fèi)者都正常運(yùn)行時,說明此時JVM虛擬機(jī)內(nèi)存里已經(jīng)存在動態(tài)生成的代理類,這時,我們就可以開始通過arthas-boot.jar工具進(jìn)行查看了。
首先,將arthas-boot.jar工具下載到你本地,我的是Windows,隨便放到一個目錄當(dāng)中,例如——

接著,直接在運(yùn)行著Dubbo消費(fèi)端進(jìn)程的IDEA上打開Terminal——

然后,輸入 java -jar C:\Users\92493\Downloads\12229238_g\arthas-boot.jar ,arthas正常運(yùn)行成功話,將列出當(dāng)前JVM上運(yùn)行的進(jìn)程——

可以看到我們剛剛啟動的provider進(jìn)程與consumer進(jìn)程,這時,只需要輸入對應(yīng)進(jìn)程前面的編號【5】,就可以將Arthas 關(guān)聯(lián)到啟動類為 org.apache.dubbo.demo.consumer.Application的 Java 進(jìn)程上了——

到這一步,我們就可以通過指令 sc *.proxy *模糊查詢帶有proxy標(biāo)志的類名了,動態(tài)代理生成的類一般都是以Proxy標(biāo)志——

其中,這里的org.apache.dubbo.common.bytecode.proxy0就是消費(fèi)者生成的動態(tài)代理類,我們可以直接反編譯去查看它內(nèi)部結(jié)構(gòu)——
[arthas@57676]$ jad org.apache.dubbo.common.bytecode.proxy0
控制臺就會打印出該動態(tài)代理類的內(nèi)部結(jié)構(gòu)——
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.alibaba.dubbo.rpc.service.EchoService
* org.apache.dubbo.common.bytecode.ClassGenerator$DC
* org.apache.dubbo.demo.DemoService
* org.apache.dubbo.rpc.service.Destroyable
*/
package org.apache.dubbo.common.bytecode;
import com.alibaba.dubbo.rpc.service.EchoService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import org.apache.dubbo.common.bytecode.ClassGenerator;
import org.apache.dubbo.demo.DemoService;
import org.apache.dubbo.rpc.service.Destroyable;
public class proxy0 implements ClassGenerator.DC,Destroyable,EchoService,DemoService {
public static Method[] methods;
private InvocationHandler handler;
public String sayHello(String string) {
Object[] objectArray = new Object[]{string};
Object object = this.handler.invoke(this, methods[0], objectArray);
return (String)object;
}
public CompletableFuture sayHelloAsync(String string) {
Object[] objectArray = new Object[]{string};
Object object = this.handler.invoke(this, methods[1], objectArray);
return (CompletableFuture)object;
}
public Object $echo(Object object) {
Object[] objectArray = new Object[]{object};
Object object2 = this.handler.invoke(this, methods[2], objectArray);
return object2;
}
public void $destroy() {
Object[] objectArray = new Object[]{};
Object object = this.handler.invoke(this, methods[3], objectArray);
}
public proxy0() {
}
public proxy0(InvocationHandler invocationHandler) {
this.handler = invocationHandler;
}
}
在Dubbo案例當(dāng)中,當(dāng)我們執(zhí)行 String message = service.sayHello("dubbo")去調(diào)用遠(yuǎn)程接口時,其實是調(diào)用了動態(tài)代理生成的方法——
public String sayHello(String string) {
Object[] objectArray = new Object[]{string};
Object object = this.handler.invoke(this, methods[0], objectArray);
return (String)object;
}
舉一反三,這個Arthas工具類可以在線上生產(chǎn)環(huán)境查看一些我們新部署的代碼,看是否是新改動的。
到此這篇關(guān)于Alibaba Java診斷工具Arthas查看Dubbo動態(tài)代理類的文章就介紹到這了,更多相關(guān)Alibaba Java診斷工具Arthas內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中使用BigDecimal進(jìn)行浮點(diǎn)數(shù)運(yùn)算
這篇文章主要介紹了Java中使用BigDecimal進(jìn)行浮點(diǎn)數(shù)運(yùn)算,需要的朋友可以參考下2014-07-07
Java程序結(jié)構(gòu)與常量變量難點(diǎn)解析
JAVA的基本結(jié)構(gòu)就是順序結(jié)構(gòu),除非特別指明,否則就按照順序一句一句執(zhí)行順序結(jié)構(gòu)是最簡單的算法結(jié)構(gòu),語句與語句之間,框與框之間是按從上到下的順序進(jìn)行的,它是由若干個依次執(zhí)行的處理步驟組成的,它是任何一個算法都離不開的一種基本算法結(jié)構(gòu)2021-10-10
SpringBoot實現(xiàn)Server-Sent Events(SSE)的使用完整指南
使用SpringBoot實現(xiàn)Server-Sent Events(SSE)可以有效處理實時數(shù)據(jù)推送需求,具有單向通信、輕量級和高實時性等優(yōu)勢,本文詳細(xì)介紹了在SpringBoot中創(chuàng)建SSE端點(diǎn)的步驟,并通過代碼示例展示了客戶端如何接收數(shù)據(jù),適用于實時通知、數(shù)據(jù)展示和在線聊天等場景2024-09-09
Java 集合實現(xiàn)分頁的方法(業(yè)務(wù)代碼實現(xiàn)分頁)
在Java開發(fā)中,有些場景比較復(fù)雜,受限制,不好在sql查詢層面實現(xiàn)分頁,需要在查詢的list結(jié)果后,將list分頁返回,如何實現(xiàn)呢,帶著這個問題一起通過本文學(xué)習(xí)吧2025-02-02
Java中CompletableFuture?的詳細(xì)介紹
這篇文章主要介紹了Java中的CompletableFuture,通過創(chuàng)建?CompletableFuture?的對象的工廠方法展開詳細(xì)的內(nèi)容介紹,需要的小伙伴可以參考一下2022-05-05
Spring Boot application.yml配置文件示例詳解
本文詳細(xì)介紹了SpringBootapplication.yml配置文件的使用和配置項,通過學(xué)習(xí)本文,您應(yīng)該已經(jīng)掌握了如何使用application.yml文件來配置SpringBoot應(yīng)用程序的不同組件,如數(shù)據(jù)源、數(shù)據(jù)庫、緩存、郵件服務(wù)等,感興趣的朋友一起看看吧2025-02-02

