詳解Alibaba?Java診斷工具Arthas查看Dubbo動態(tài)代理類
閱讀Dubbo源碼過程中,會發(fā)現(xiàn),Dubbo消費端在做遠程調(diào)用時,默認通過 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)然,它的功能遠不止此,還可以在生產(chǎn)環(huá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)的運行狀況?
有什么辦法可以監(jiān)控到JVM的實時運行狀態(tài)?
怎么快速定位應(yīng)用的熱點,生成火焰圖?
怎樣直接從JVM內(nèi)查找某個類的實例?
這些方案本文暫不展開,這里只展開通過該工具查看Dubbo生成的動態(tài)代理類。
我是直接在使用dubbo-parent源碼中的例子,分別啟動了提供者與消費者。
首先,啟動提供者方法——
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; }
接著,啟動消費者,這里可以設(shè)置一個休眠時間,這樣就可以一直維持消費者運行在內(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ù)提供者與消費者都正常運行時,說明此時JVM虛擬機內(nèi)存里已經(jīng)存在動態(tài)生成的代理類,這時,我們就可以開始通過arthas-boot.jar工具進行查看了。
首先,將arthas-boot.jar工具下載到你本地,我的是Windows,隨便放到一個目錄當(dāng)中,例如——
接著,直接在運行著Dubbo消費端進程的IDEA上打開Terminal——
然后,輸入 java -jar C:\Users\92493\Downloads\12229238_g\arthas-boot.jar ,arthas正常運行成功話,將列出當(dāng)前JVM上運行的進程——
可以看到我們剛剛啟動的provider進程與consumer進程,這時,只需要輸入對應(yīng)進程前面的編號【5】,就可以將Arthas 關(guān)聯(lián)到啟動類為 org.apache.dubbo.demo.consumer.Application的 Java 進程上了——
到這一步,我們就可以通過指令 sc *.proxy *模糊查詢帶有proxy標志的類名了,動態(tài)代理生成的類一般都是以Proxy標志——
其中,這里的org.apache.dubbo.common.bytecode.proxy0就是消費者生成的動態(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)用遠程接口時,其實是調(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中常見XML解析器的使用詳解(JAXP,DOM4J,Jsoup,JsoupXPath)
為了處理和操作XML數(shù)據(jù),我們需要使用XML解析器,本文將介紹幾種常用的XML解析器,包括JAXP、DOM4J、Jsoup和JsoupXPath,需要的小伙伴可以參考一下2023-11-11springboot中使用ConstraintValidatorContext驗證兩個字段內(nèi)容相同
開發(fā)修改密碼功能時,通過ConstraintValidator校驗新密碼和確認新密碼的一致性,首先定義Matches注解和DTO對象,然后創(chuàng)建MatchesValidator類實現(xiàn)驗證邏輯,對springboot驗證字段內(nèi)容相同問題感興趣的朋友一起看看吧2024-10-10Java?事務(wù)注解@Transactional回滾(try?catch、嵌套)問題
這篇文章主要介紹了Java?@Transactional回滾(try?catch、嵌套)問題,Spring?事務(wù)注解?@Transactional?本來可以保證原子性,如果事務(wù)內(nèi)有報錯的話,整個事務(wù)可以保證回滾,但是加上try?catch或者事務(wù)嵌套,可能會導(dǎo)致事務(wù)回滾失敗2022-08-08WebUploader客戶端批量上傳圖片 后臺使用springMVC
這篇文章主要為大家詳細介紹了WebUploader客戶端批量上傳圖片,后臺使用springMVC接收實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-09-09SpringBoot中使用Servlet三大組件的方法(Servlet、Filter、Listener)
這篇文章主要介紹了SpringBoot中使用Servlet三大組件的方法(Servlet、Filter、Listener),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01