详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类


Posted in Java/Android onApril 08, 2022

阅读Dubbo源码过程中,会发现,Dubbo消费端在做远程调用时,默认通过 Javassist 框架为服务接口生成动态代理类,调用javassist框架下的JavassistProxyFactory类的getProxy(Invoker invoker, Class<?>[] interfaces)方法,动态生成一个存放在JVM中的动态代理类。

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
    return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}

那么,问题来了,如果我们想要一睹该动态生成的代理类内部结构是怎样的,如何才能便捷做到的?

这就是我想介绍的一款工具,它可以帮助我们查看JDK或者javassist生成的动态代理类,当然,它的功能远不止此,还可以在生产环境进行诊断。

Arthas 是Alibaba开源的Java诊断工具,官方在线文档地址:https://arthas.aliyun.com/doc/

根据官网上的介绍,它还可以解决以下问题————

当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

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

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

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

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

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

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

怎么快速定位应用的热点,生成火焰图?

怎样直接从JVM内查找某个类的实例?

这些方案本文暂不展开,这里只展开通过该工具查看Dubbo生成的动态代理类。

我是直接在使用dubbo-parent源码中的例子,分别启动了提供者与消费者。

详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类

首先,启动提供者方法——

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")设置成你本地内网的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;

}

接着,启动消费者,这里可以设置一个休眠时间,这样就可以一直维持消费者运行在内存当中——

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();
        }
}

当Dubbo的服务提供者与消费者都正常运行时,说明此时JVM虚拟机内存里已经存在动态生成的代理类,这时,我们就可以开始通过arthas-boot.jar工具进行查看了。

首先,将arthas-boot.jar工具下载到你本地,我的是Windows,随便放到一个目录当中,例如——

详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类

接着,直接在运行着Dubbo消费端进程的IDEA上打开Terminal——

详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类

然后,输入 java -jar C:\Users\92493\Downloads\12229238_g\arthas-boot.jar ,arthas正常运行成功话,将列出当前JVM上运行的进程——

详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类

可以看到我们刚刚启动的provider进程与consumer进程,这时,只需要输入对应进程前面的编号【5】,就可以将Arthas 关联到启动类为 org.apache.dubbo.demo.consumer.Application的 Java 进程上了——

详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类

到这一步,我们就可以通过指令 sc *.proxy *模糊查询带有proxy标志的类名了,动态代理生成的类一般都是以Proxy标志——

详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类

其中,这里的org.apache.dubbo.common.bytecode.proxy0就是消费者生成的动态代理类,我们可以直接反编译去查看它内部结构——

[arthas@57676]$ jad org.apache.dubbo.common.bytecode.proxy0

控制台就会打印出该动态代理类的内部结构——

/*
 * 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案例当中,当我们执行 String message = service.sayHello("dubbo")去调用远程接口时,其实是调用了动态代理生成的方法——

public String sayHello(String string) {
    Object[] objectArray = new Object[]{string};
    Object object = this.handler.invoke(this, methods[0], objectArray);
    return (String)object;
}

举一反三,这个Arthas工具类可以在线上生产环境查看一些我们新部署的代码,看是否是新改动的。

到此这篇关于Alibaba Java诊断工具Arthas查看Dubbo动态代理类的文章就介绍到这了,更多相关Alibaba Java诊断工具Arthas内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
SpringBoot集成Redis,并自定义对象序列化操作
Jun 22 Java/Android
Java基于字符界面的简易收银台
Jun 26 Java/Android
详解Spring Boot使用系统参数表提升系统的灵活性
Jun 30 Java/Android
Springboot配置suffix指定mvc视图的后缀方法
Jul 03 Java/Android
dubbo服务整合zipkin详解
Jul 26 Java/Android
Spring Boot 排除某个类加载注入IOC的操作
Aug 02 Java/Android
maven依赖的version声明控制方式
Jan 18 Java/Android
springmvc直接不经过controller访问WEB-INF中的页面问题
Feb 24 Java/Android
SpringBoot中HttpSessionListener的简单使用方式
Mar 17 Java/Android
Android开发手册自定义Switch开关按钮控件
Jun 10 Java/Android
Java异常体系非正常停止和分类
Jun 14 Java/Android
httpclient调用远程接口的方法
Aug 14 Java/Android
SpringCloud Function SpEL注入漏洞分析及环境搭建
SpringBoot中获取profile的方法详解
Apr 08 #Java/Android
教你在 Java 中实现 Dijkstra 最短路算法的方法
Java 垃圾回收超详细讲解记忆集和卡表
Java 常见的限流算法详细分析并实现
Java 超详细讲解ThreadLocal类的使用
Java 通过手写分布式雪花SnowFlake生成ID方法详解
You might like
cache_lite试用
2007/02/14 PHP
php GD绘制24小时柱状图
2008/06/28 PHP
php文件打包 下载之使用PHP自带的ZipArchive压缩文件并下载打包好的文件
2012/06/13 PHP
php删除与复制文件夹及其文件夹下所有文件的实现代码
2013/01/23 PHP
神盾加密解密教程(三)PHP 神盾解密工具
2014/06/08 PHP
PHP 实现页面静态化的几种方法
2017/07/23 PHP
YII2框架中behavior行为的理解与使用方法示例
2020/03/13 PHP
Laravel框架下的Contracts契约详解
2020/03/17 PHP
javascript 图片上一张下一张链接效果代码
2010/03/12 Javascript
JS 如果改变span标签的是否隐藏属性
2011/10/06 Javascript
JS+CSS实现自动切换的网页滑动门菜单效果代码
2015/09/14 Javascript
JS代码实现根据时间变换页面背景效果
2016/06/16 Javascript
原生JS简单实现ajax的方法示例
2016/11/29 Javascript
Angular.JS中select下拉框设置value的方法
2017/06/20 Javascript
vue实现表格增删改查效果的实例代码
2017/07/18 Javascript
限时抢购-倒计时的完整实例(分享)
2017/09/17 Javascript
详解使用PM2管理nodejs进程
2017/10/24 NodeJs
vue2 前端搜索实现示例
2018/02/26 Javascript
JS实现生成由字母与数字组合的随机字符串功能详解
2018/05/25 Javascript
vue组件从开发到发布的实现步骤
2018/11/11 Javascript
Next.js实现react服务器端渲染的方法示例
2019/01/06 Javascript
[36:37]2014 DOTA2华西杯精英邀请赛5 24 VG VS iG
2014/05/25 DOTA
[02:28]DOTA2 2015国际邀请赛中国区预选赛首日现场百态
2015/05/26 DOTA
在Python中操作时间之mktime()方法的使用教程
2015/05/22 Python
python获取时间及时间格式转换问题实例代码详解
2018/12/06 Python
Django集成搜索引擎Elasticserach的方法示例
2019/06/04 Python
Python使用scrapy爬取阳光热线问政平台过程解析
2019/08/14 Python
Python实现动态给类和对象添加属性和方法操作示例
2020/02/29 Python
Python实现播放和录制声音的功能
2020/08/12 Python
德国最新街头服饰网上商店:BODYCHECK
2019/09/15 全球购物
申请任职学生会干部自荐书范文
2014/02/13 职场文书
学雷锋主题班会教案
2015/08/13 职场文书
CSS3点击按钮圆形进度打钩效果的实现代码
2021/03/30 HTML / CSS
go语言中切片与内存复制 memcpy 的实现操作
2021/04/27 Golang
linux下安装redis图文详细步骤
2021/12/04 Redis
Nginx 匹配方式
2022/05/15 Servers