`
xiangshouxiyang
  • 浏览: 49244 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

动态代理在RPC框架中的性能对比

阅读更多

       这几天打算自己写一个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动态代理,简单省事,性能又好,又非常的可靠稳定。

分享到:
评论
1 楼 pch272215690 2019-01-25  
微博的motan目的就是精简,事实上确实简单了很多,毕竟是站在别人肩膀上的

相关推荐

    如何用Netty写一个自己的RPC框架

    3. RPC框架的工作机制:RPC框架允许一个应用调用另一个地址空间(通常是远程)中的方法。它主要依赖于网络通信、数据序列化和反序列化、动态代理、服务注册与发现等机制。 4. Java NIO(New I/O):NIO是Java提供的...

    Java 动态代理详解(代理模式+静态代理+JDK动态代理+CGLIB动态代理)

    Java 动态代理是 Java 编程语言中的一种强大工具,广泛应用于 Spring AOP、Hibernate 数据查询、测试框架的后端 mock、RPC 远程调用、Java 注解对象获取、日志、用户鉴权、全局性异常处理、性能监控等领域。...

    Go-rpcx是一个类似Dubbo和Motan的分布式的RPC服务框架

    - **高性能**: Go-rpcx充分利用了Golang的并发模型,通过goroutine和通道(channel)实现了高效的并发处理,相比其他RPC框架,如gRPC,它在某些场景下表现出更优的性能。 - **插件化设计**: rpcx 支持插件扩展,允许...

    JSONRPC

    4. **动态代理**:JAVA的动态代理技术可以用于实现JSONRPC的自动调用,简化客户端代码。 **集成与安全** 1. **Spring集成**:可以通过Spring框架轻松集成JSONRPC服务,利用Spring的强大功能进行依赖注入和事务管理...

    REST与RPC的区别.pdf

    - **RPC**:更适合于内部服务间的调用,特别是在性能敏感的应用场景中。 #### 五、总结 综上所述,REST和RPC各有优势和局限性。REST以其简洁的设计和广泛的适用性,在构建开放式的API方面表现出色;而RPC则以其高...

    基于Java的brap(Java远程调用框架 BRAP).zip

    - **与Dubbo对比**:Dubbo是阿里巴巴开源的RPC框架,同样具有高性能和丰富的服务治理功能。但BRAP更轻量,更适合对性能有较高要求且不需复杂服务治理的场景。 - **与gRPC对比**:gRPC是Google开源的RPC框架,...

    rpcstudy:学习rpc

    在Java中,RPC框架有很多,如Hessian、RMI(Java Remote Method Invocation)、gRPC、Dubbo、Spring Cloud等。这些框架各有特点,但基本原理相似:客户端发起请求,通过网络传输到服务端,服务端处理请求后返回结果...

    high_performance_rpc_with_netty

    同时,我们将剖析Netty在分布式服务框架中的应用,以及其与其他网络I/O模型的比较。 【部分内容】: 1. **什么是Netty?能做什么?** Netty是一个高度灵活、高性能的网络应用框架,用于构建可扩展的网络服务器和...

    RMI与RPC

    Dubbo作为一款流行的RPC框架,也是基于这一协议。 【RMI实现原理】 RMI的工作流程主要包括以下几个步骤: 1. **服务器端**:创建一个实现了特定接口的远程对象,并将其注册到RMI Registry中,使得客户端可以通过...

    微服务架构选型spring boot 与dubbo的比较

    在众多微服务框架中,Spring Boot 和 Dubbo 是两个备受欢迎的选择。本文将深入探讨这两种框架的特点,并从多个维度进行比较分析。 #### 二、微服务框架概述 ##### 1. Dubbo Dubbo 是阿里巴巴开源的一款分布式服务...

    基于Dubbo的分布式框架研究.docx

    4.流动计算架构a)在大规模分布式服务的基础上,进一步抽象出流动计算框架,使得服务可以跨数据中心迁移,实现故障自动恢复和资源动态调度。 (2)架构和原理Dubbo采用了基于接口的代理实现,服务提供者暴露服务,...

    hessian案例,hessian案例

    5. **性能比较**:压缩包内的"java 几种远程服务调用协议的比较.txt"可能包含关于Hessian与其他RPC协议(如RMI、SOAP、gRPC等)的性能对比。通常,Hessian因其二进制格式,相比基于XML的协议(如SOAP)在速度和带宽...

    .NET2.0环境下的Ajax选型和应用(提供Demo源码下载)

    在这个研究中,Jimmy.Ke探讨了几个关键问题,包括Ajax应用框架的选择,如何在.NET 2.0环境中应用Ajax,以及在实施过程中需要注意的事项。 首先,Ajax应用框架的选型是关键。研究提供了三个不同的框架示例:微软的...

    Dubbo服务框架面试专题及答案整理文档.zip

    Dubbo服务框架是阿里巴巴开源的一款高性能、轻量级的服务治理框架,它主要应用于分布式环境中的服务调用。这个“Dubbo服务框架面试专题及答案整理文档”包含了关于Dubbo的各种面试常见问题及其解答,旨在帮助求职者...

    大数据面试知识点

    HDFS BlockReader是Hadoop分布式文件系统中用于读取数据块的组件,它使得MapReduce等大数据处理框架能够高效地读取和处理存储在HDFS上的数据。 Hive是一个数据仓库软件,提供数据存储、查询和分析的功能。Hive的...

    activemq,dubbo,linux,redis,shiro,solr笔记整合,基本都是面试会问到的经典题型

    **Dubbo** 是阿里巴巴开源的高性能Java RPC框架,它使得服务提供者可以在一个独立的进程中暴露服务,而服务消费者可以透明地调用这些服务。面试中可能涉及的话题包括服务注册与发现、负载均衡策略、服务治理(如熔断...

    1000道互联网大厂Java工程师面试题及答案

    面试中会考察它们的使用场景、性能对比及常见操作。 【MySQL】 作为常用的数据库,MySQL的面试题目涵盖索引类型、事务处理、锁机制、性能优化(如EXPLAIN分析)以及复制、分区等高级特性。 【Spring/Spring Boot/...

    SOAP webserivce 和 RESTful webservice 对比及区别

    相比之下,SOAP服务的安全控制通常涉及更复杂的XML消息验证和处理,因为所有的业务逻辑和安全信息都封装在SOAP消息中。这可能导致在现有的HTTP代理服务器上实现安全策略时遇到困难,因为代理可能无法直接解析和处理...

    Dubbo高级视频教程

    - **Dubbo简介**:Dubbo是一款高性能、轻量级的开源Java RPC框架,旨在为服务治理提供简单、全面的解决方案。 - **分布式系统概念**:分布式系统是由多台计算机组成的系统,这些计算机通过网络进行通信并协同完成...

Global site tag (gtag.js) - Google Analytics