这几天打算自己写一个RPC框架,参考了阿里的dubbo,微博的motan等框架的设计。在选择代理技术时,dubbo有两种方案,一个是jdk的动态代理,一个JAVAASSIST的字节码生成技术,在dubbo作者梁飞的博客《动态代理方案性能对比》http://javatar.iteye.com/blog/814426中,因为作者在编写服务框架需要用动态代理生成客户端接口的stub,进行了几个动态代理方案性能的测试,测试表明了JAVAASSIST的字节码生成技术的性能应该是最好的。而微博的motan框架里,只使用了jdk代理技术。我脑海里就有疑问,为什么motan框架不用JAVAASSIST的字节码生成技术呢。所以我干脆把几种动态代理技术都用上,测试下在RPC框架中,几种动态代理的性能如何。
下面是测试用例:
maven依赖的包如下:
<dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.20.0-GA</version> </dependency> <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> <version>1.6.3</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.2</version> </dependency> <dependency>
ProxyEnum:其中javassist字节码生成技术由于需要拼接字符串,比较复杂,所以我把dubbo中这部分的源码扒出来直接拿来用了。
package com.hcd.melody.proxy; import com.hcd.melody.common.util.ReflectUtil; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyObject; import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.matcher.ElementMatchers; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * 代理技术枚举类 * Created by cd_huang on 2017/6/3. */ public enum ProxyEnum { JDK_PROXY(new ProxyFactory() { public <T> T newProxyInstance(Class<T> inferfaceClass, Object handler) { return inferfaceClass.cast(Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {inferfaceClass}, (InvocationHandler)handler)); } }), BYTE_BUDDY_PROXY(new ProxyFactory() { public <T> T newProxyInstance(Class<T> inferfaceClass, Object handler) { Class<? extends T> cls = new ByteBuddy() .subclass(inferfaceClass) .method(ElementMatchers.isDeclaredBy(inferfaceClass)) .intercept(MethodDelegation.to(handler, "handler")) .make() .load(inferfaceClass.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded(); return ReflectUtil.newInstance(cls); } }), CGLIB_PROXY(new ProxyFactory() { public <T> T newProxyInstance(Class<T> inferfaceClass, Object handler) { Enhancer enhancer = new Enhancer(); enhancer.setCallback((MethodInterceptor)handler); enhancer.setInterfaces(new Class[] {inferfaceClass}); return (T) enhancer.create(); } }), JAVASSIST_BYTECODE_PROXY(new ProxyFactory() { public <T> T newProxyInstance(Class<T> inferfaceClass, Object handler) { return (T) com.hcd.melody.proxy.bytecode.Proxy.getProxy(inferfaceClass).newInstance((InvocationHandler)handler); } }), JAVASSIST_DYNAMIC_PROXY(new ProxyFactory() { public <T> T newProxyInstance(Class<T> inferfaceClass, Object handler) { javassist.util.proxy.ProxyFactory proxyFactory = new javassist.util.proxy.ProxyFactory(); proxyFactory.setInterfaces(new Class[] { inferfaceClass }); Class<?> proxyClass = proxyFactory.createClass(); T javassistProxy = (T)ReflectUtil.newInstance(proxyClass); ((ProxyObject) javassistProxy).setHandler((MethodHandler)handler); return javassistProxy; } }); private ProxyFactory factory; ProxyEnum(ProxyFactory factory){ this.factory =factory; } public <T> T newProxyInstance(Class<T> interfaceType, Object handler) { return factory.newProxyInstance(interfaceType, handler); } interface ProxyFactory{ <T> T newProxyInstance(Class<T> inferfaceClass, Object handler); } }
InvokeHandler:
package com.hcd.melody.proxy; import javassist.util.proxy.MethodHandler; import net.bytebuddy.implementation.bind.annotation.AllArguments; import net.bytebuddy.implementation.bind.annotation.Origin; import net.bytebuddy.implementation.bind.annotation.RuntimeType; import net.bytebuddy.implementation.bind.annotation.This; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 客户端代理类的处理 * Created by cd_huang on 2017/6/3. */ public class InvokeHandler implements InvocationHandler, MethodHandler, MethodInterceptor { private Object doInvoke(Object proxy, Method method, Object[] args) { if (Object.class == method.getDeclaringClass()) { String name = method.getName(); if ("equals".equals(name)) { return proxy == args[0]; } else if ("hashCode".equals(name)) { return System.identityHashCode(proxy); } else if ("toString".equals(name)) { return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy)) + ", with InvokeHandler " + this; } else { throw new IllegalStateException(String.valueOf(method)); } } //TODO RPC调用 return null; } /** * jdk invoke * * @param proxy * @param method * @param args * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return doInvoke(proxy, method, args); } /** * byteBuddy invoke * * @param proxy * @param method * @param args * @return * @throws Throwable */ @RuntimeType public Object byteBuddyInvoke(@This Object proxy, @Origin Method method, @AllArguments @RuntimeType Object[] args) throws Throwable { return doInvoke(proxy, method, args); } /** * javassist invoke * * @param proxy * @param method * @param proceed * @param args * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Method proceed, Object[] args) throws Throwable { return doInvoke(proxy, method, args); } /** * cglib invoke * * @param proxy * @param method * @param args * @param methodProxy * @return * @throws Throwable */ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return doInvoke(proxy, method, args); } }
TestService:
package com.hcd.melody.test; /** * test接口类 * Created by cd_huang on 2017/6/3. */ public interface TestService { String test(String s); }
TestProxy:
package com.hcd.melody.test; import com.hcd.melody.proxy.*; /** * 测试动态代理技术区别 * Created by cd_huang on 2017/6/3. */ public class TestProxy { public static void main(String args[]) { InvokeHandler handler =new InvokeHandler(); long time = System.currentTimeMillis(); TestService test1 =ProxyEnum.JDK_PROXY.newProxyInstance(TestService.class,handler); time = System.currentTimeMillis() - time; System.out.println("Create JDK Proxy: " + time + " ms"); time = System.currentTimeMillis(); TestService test2 =ProxyEnum.BYTE_BUDDY_PROXY.newProxyInstance(TestService.class,handler); time = System.currentTimeMillis() - time; System.out.println("Create byteBuddy Proxy: " + time + " ms"); time = System.currentTimeMillis(); TestService test3 =ProxyEnum.CGLIB_PROXY.newProxyInstance(TestService.class,handler); time = System.currentTimeMillis() - time; System.out.println("Create CGLIB Proxy: " + time + " ms"); time = System.currentTimeMillis(); TestService test4 =ProxyEnum.JAVASSIST_BYTECODE_PROXY.newProxyInstance(TestService.class,handler); time = System.currentTimeMillis() - time; System.out.println("Create JAVASSIST Bytecode Proxy: " + time + " ms"); time = System.currentTimeMillis(); TestService test5 =ProxyEnum.JAVASSIST_DYNAMIC_PROXY.newProxyInstance(TestService.class,handler); time = System.currentTimeMillis() - time; System.out.println("Create JAVASSIST Proxy: " + time + " ms"); String s ="proxy"; System.out.println("----------------"); for (int i = 0; i <10; i++) { test(test1, "Run JDK Proxy: ",s); test(test2, "Run byteBuddy Proxy: ",s); test(test3, "Run CGLIB Proxy: ",s); test(test4, "Run JAVASSIST Bytecode Proxy: ",s); test(test5, "Run JAVASSIST Proxy: ",s); System.out.println("----------------"); } } private static void test(TestService service, String label,String s) { service.test(s); // warm up int count = 100000000; long time = System.currentTimeMillis(); for (int i = 0; i < count; i++) { service.test(s); } time = System.currentTimeMillis() - time; System.out.println(label + time + " ms, "); } }
测试结果如下:
Create JDK Proxy: 26 ms Create byteBuddy Proxy: 639 ms Create CGLIB Proxy: 103 ms Create JAVASSIST Bytecode Proxy: 164 ms Create JAVASSIST Proxy: 17 ms ---------------- Run JDK Proxy: 21 ms, Run byteBuddy Proxy: 241 ms, Run CGLIB Proxy: 951 ms, Run JAVASSIST Bytecode Proxy: 918 ms, Run JAVASSIST Proxy: 827 ms, ---------------- Run JDK Proxy: 499 ms, Run byteBuddy Proxy: 632 ms, Run CGLIB Proxy: 547 ms, Run JAVASSIST Bytecode Proxy: 602 ms, Run JAVASSIST Proxy: 718 ms, ---------------- Run JDK Proxy: 544 ms, Run byteBuddy Proxy: 567 ms, Run CGLIB Proxy: 563 ms, Run JAVASSIST Bytecode Proxy: 586 ms, Run JAVASSIST Proxy: 724 ms, ---------------- Run JDK Proxy: 550 ms, Run byteBuddy Proxy: 553 ms, Run CGLIB Proxy: 555 ms, Run JAVASSIST Bytecode Proxy: 585 ms, Run JAVASSIST Proxy: 761 ms, ---------------- Run JDK Proxy: 532 ms, Run byteBuddy Proxy: 552 ms, Run CGLIB Proxy: 528 ms, Run JAVASSIST Bytecode Proxy: 601 ms, Run JAVASSIST Proxy: 724 ms, ---------------- Run JDK Proxy: 545 ms, Run byteBuddy Proxy: 518 ms, Run CGLIB Proxy: 518 ms, Run JAVASSIST Bytecode Proxy: 603 ms, Run JAVASSIST Proxy: 715 ms, ---------------- Run JDK Proxy: 544 ms, Run byteBuddy Proxy: 544 ms, Run CGLIB Proxy: 544 ms, Run JAVASSIST Bytecode Proxy: 571 ms, Run JAVASSIST Proxy: 727 ms, ---------------- Run JDK Proxy: 546 ms, Run byteBuddy Proxy: 589 ms, Run CGLIB Proxy: 536 ms, Run JAVASSIST Bytecode Proxy: 577 ms, Run JAVASSIST Proxy: 729 ms, ---------------- Run JDK Proxy: 667 ms, Run byteBuddy Proxy: 632 ms, Run CGLIB Proxy: 508 ms, Run JAVASSIST Bytecode Proxy: 596 ms, Run JAVASSIST Proxy: 708 ms, ---------------- Run JDK Proxy: 541 ms, Run byteBuddy Proxy: 532 ms, Run CGLIB Proxy: 533 ms, Run JAVASSIST Bytecode Proxy: 613 ms, Run JAVASSIST Proxy: 761 ms, ----------------
从上面的测试,我得出来的结论和梁飞完全不同,几种动态代理技术的性能差异不大,JDK Proxy、byteBuddy Proxy、CGLIB Proxy的性能会比JAVAASSIST Bytecode Proxy、JAVAASSIST Proxy稍微快一些,而梁飞的结论是javaassist字节码生成技术的性能是最好的。但是上面看来,javaassist字节码生成技术的性能是比较差的。为什么会有这个区别呢。是因为在梁飞的例子里,会对目标对象delegate进行调用。而jdk动态代理,cglib动态代理,javaassist动态代理,都会使用反射调用目标对象,而javaassist字节码生成技术,asm字节码生成技术,则没有使用反射调用目标对象,而是类似装饰器的设计模式,直接调用delegate.method(args)。所以,性能的差异在于用反射调用方法和直接调用。而在RPC框架或分布式服务框架中,并不需要用反射去调用某个类,而是拿到接口名,方法,以及参数,把这些信息发送给服务端让服务端去反射调用类,再把结果返回到客户端。所以,在我看来,dubbo框架的客户端中使用javaassist字节码生成技术完全没有必要,字节码生成技术的优势在于避免反射调用方法,而RPC框架的客户端则没有这样的场景。像微博的motan框架那样直接jdk动态代理,简单省事,性能又好,又非常的可靠稳定。
相关推荐
3. RPC框架的工作机制:RPC框架允许一个应用调用另一个地址空间(通常是远程)中的方法。它主要依赖于网络通信、数据序列化和反序列化、动态代理、服务注册与发现等机制。 4. Java NIO(New I/O):NIO是Java提供的...
Java 动态代理是 Java 编程语言中的一种强大工具,广泛应用于 Spring AOP、Hibernate 数据查询、测试框架的后端 mock、RPC 远程调用、Java 注解对象获取、日志、用户鉴权、全局性异常处理、性能监控等领域。...
- **高性能**: Go-rpcx充分利用了Golang的并发模型,通过goroutine和通道(channel)实现了高效的并发处理,相比其他RPC框架,如gRPC,它在某些场景下表现出更优的性能。 - **插件化设计**: rpcx 支持插件扩展,允许...
4. **动态代理**:JAVA的动态代理技术可以用于实现JSONRPC的自动调用,简化客户端代码。 **集成与安全** 1. **Spring集成**:可以通过Spring框架轻松集成JSONRPC服务,利用Spring的强大功能进行依赖注入和事务管理...
- **RPC**:更适合于内部服务间的调用,特别是在性能敏感的应用场景中。 #### 五、总结 综上所述,REST和RPC各有优势和局限性。REST以其简洁的设计和广泛的适用性,在构建开放式的API方面表现出色;而RPC则以其高...
- **与Dubbo对比**:Dubbo是阿里巴巴开源的RPC框架,同样具有高性能和丰富的服务治理功能。但BRAP更轻量,更适合对性能有较高要求且不需复杂服务治理的场景。 - **与gRPC对比**:gRPC是Google开源的RPC框架,...
在Java中,RPC框架有很多,如Hessian、RMI(Java Remote Method Invocation)、gRPC、Dubbo、Spring Cloud等。这些框架各有特点,但基本原理相似:客户端发起请求,通过网络传输到服务端,服务端处理请求后返回结果...
同时,我们将剖析Netty在分布式服务框架中的应用,以及其与其他网络I/O模型的比较。 【部分内容】: 1. **什么是Netty?能做什么?** Netty是一个高度灵活、高性能的网络应用框架,用于构建可扩展的网络服务器和...
Dubbo作为一款流行的RPC框架,也是基于这一协议。 【RMI实现原理】 RMI的工作流程主要包括以下几个步骤: 1. **服务器端**:创建一个实现了特定接口的远程对象,并将其注册到RMI Registry中,使得客户端可以通过...
在众多微服务框架中,Spring Boot 和 Dubbo 是两个备受欢迎的选择。本文将深入探讨这两种框架的特点,并从多个维度进行比较分析。 #### 二、微服务框架概述 ##### 1. Dubbo Dubbo 是阿里巴巴开源的一款分布式服务...
4.流动计算架构a)在大规模分布式服务的基础上,进一步抽象出流动计算框架,使得服务可以跨数据中心迁移,实现故障自动恢复和资源动态调度。 (2)架构和原理Dubbo采用了基于接口的代理实现,服务提供者暴露服务,...
5. **性能比较**:压缩包内的"java 几种远程服务调用协议的比较.txt"可能包含关于Hessian与其他RPC协议(如RMI、SOAP、gRPC等)的性能对比。通常,Hessian因其二进制格式,相比基于XML的协议(如SOAP)在速度和带宽...
在这个研究中,Jimmy.Ke探讨了几个关键问题,包括Ajax应用框架的选择,如何在.NET 2.0环境中应用Ajax,以及在实施过程中需要注意的事项。 首先,Ajax应用框架的选型是关键。研究提供了三个不同的框架示例:微软的...
Dubbo服务框架是阿里巴巴开源的一款高性能、轻量级的服务治理框架,它主要应用于分布式环境中的服务调用。这个“Dubbo服务框架面试专题及答案整理文档”包含了关于Dubbo的各种面试常见问题及其解答,旨在帮助求职者...
HDFS BlockReader是Hadoop分布式文件系统中用于读取数据块的组件,它使得MapReduce等大数据处理框架能够高效地读取和处理存储在HDFS上的数据。 Hive是一个数据仓库软件,提供数据存储、查询和分析的功能。Hive的...
**Dubbo** 是阿里巴巴开源的高性能Java RPC框架,它使得服务提供者可以在一个独立的进程中暴露服务,而服务消费者可以透明地调用这些服务。面试中可能涉及的话题包括服务注册与发现、负载均衡策略、服务治理(如熔断...
面试中会考察它们的使用场景、性能对比及常见操作。 【MySQL】 作为常用的数据库,MySQL的面试题目涵盖索引类型、事务处理、锁机制、性能优化(如EXPLAIN分析)以及复制、分区等高级特性。 【Spring/Spring Boot/...
相比之下,SOAP服务的安全控制通常涉及更复杂的XML消息验证和处理,因为所有的业务逻辑和安全信息都封装在SOAP消息中。这可能导致在现有的HTTP代理服务器上实现安全策略时遇到困难,因为代理可能无法直接解析和处理...
- **Dubbo简介**:Dubbo是一款高性能、轻量级的开源Java RPC框架,旨在为服务治理提供简单、全面的解决方案。 - **分布式系统概念**:分布式系统是由多台计算机组成的系统,这些计算机通过网络进行通信并协同完成...