阅读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内查找某个类的实例?
【Alibaba Java诊断工具Arthas查看Dubbo动态代理类】这些方案本文暂不展开,这里只展开通过该工具查看Dubbo生成的动态代理类 。
我是直接在使用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")设置成你本地内网的ip即可;DemoServiceImpl类详情——
public class DemoServiceImpl implements DemoService {private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);@Overridepublic String sayHello(String name) {logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();}@Overridepublic 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工具进行查看了 。
推荐阅读
- Java JVM启动参数大全
- 14个Java并发容器,你用过几个?
- JavaScript 中对于Promise的理解
- JAVA快速入门——算数运算符
- Java I/O 入门篇
- 深入理解Java继承的实现原理
- Java-背包算法实现
- JavaScript 中的位运算和权限设计
- JAVA中ArrayList、LinkedList、Vector、Stack的比较
- javascript 类型的隐式转换