自己实现一个RPC框架

简介: <h2>RPC简介</h2> <div><img src="http://img.blog.csdn.net/20141020113519167?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGhyZWVfbWFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravit

RPC简介


远程调用协议。 有多有用,多普遍就不摆了。
大概过程:
1. 调用客户端句柄,传输参数。
2. 封装参数等消息为网络传输格式传给远程主机
3. 服务器句柄得到消息并解析参数
4. 在服务器段执行要调用的代码,并把结果返回给服务器句柄
5. 服务器句柄封装返回结果并用网络传输给客户端
6. 客户端解析并进行其他处理
可见之问题主要有,通信方式、句柄实现、以及消息封装和解析(序列化及反序列化)

RPC 之 通信

直接用Socket走TCP/IP 或者是UDP协议, 例如RMI

走HTTP协议,  Hessian。 好处不用防火墙再开端口了

在下面的例子中,使用java socket。这样可以看到更基础的内容。


RPC 之动态代理

句柄部分我们可以使用动态代理,在调用客户端方法的时候使用动态代理来调用远程方法


RPC 之序列化与反序列化

这个也有很多种,比如Java自带的序列化机制,实现了Serializable接口的类,都能够进行序列化

对于Java序列化的原理可以看下下面的博文:

http://www.java3z.com/cwbwebhome/article/article8/862.html

是Java处理二进制的一贯风格,定义了标志位表示后面有多长的内容表示了一个什么,然后是具体的内容。


Java的反序列化。

最好的办法当然是看ObjectInputStream的源代码了。

大概的流程是:

1. readObject0  入口

2. 然后就是一段一段读取然后解析了。一般最开始读取Class readClass,读取这个类的Class接口

3. readOrdinaryObject 使用newInstance来创建实例

4. 然后用defaultReadFields 中的desc.setObjFieldValues内容来个这个实例的字段赋值。

5. 这是一个循环的过程,知道把对象中所有的内容都还原。


还可以有其他的方式,比如WebService技术是使用了Soap协议来进行传输的,使用的是类似于XML格式的方式描述消息内容的(因此消息内容会比较臃肿)


Hessian是使用了二进制的方式进行了序列化,它使用了自己的规则,该规则可以用其他语言实现,是一个简单而且高效的协议。

细节链接如下:http://hessian.caucho.com/doc/hessian-serialization.html


在下面的例子中使用ObjectInputStream来进行序列化操作。


自己的RPC框架

服务器端

启动Socket并且给每个请求创建一个处理线程:

public class StartUp {
	public static final int port = 8080;
	
	public static void main(String[] args) {
			exportRpc();
	}
	
	/**
	 * 导出RPC接口
	 */
	private static void exportRpc() {
		try {
			ServerSocket ss = new ServerSocket(9001);
			while(true){
				Socket s = ss.accept();
				new RpcThread(s).start();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

然后定义传递的消息内容,要包含了客户端要告诉服务器端的内容,最少要包含,调用的接口名,调用的方法名,以及方法参数。该对象如下:

public class RpcObject implements Serializable{
	private static final long serialVersionUID = 1L;
	private Class c;
	private String methodName;
	private Object[] args;
	
	public RpcObject() {
	}
	
	public RpcObject(Class c, String methodName, Object[] args) {
		this.c = c;
		this.methodName = methodName;
		this.args = args;
	}
	public Class getC() {
		return c;
	}
	public void setC(Class c) {
		this.c = c;
	}
	public String getMethodName() {
		return methodName;
	}
	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}
	public Object[] getArgs() {
		return args;
	}
	public void setArgs(Object[] args) {
		this.args = args;
	}
}

服务器端处理线程,将会出去参数对象,然后通过反射来得到具体的实例对象以及调用需要调用的方法。

public class RpcThread extends Thread {
	private Socket s;
	public RpcThread(Socket s) {
		this.s = s;
	}
	
	@Override
	public void run() {
		ObjectInputStream is = null;
		ObjectOutputStream os = null;
		try {
			is = new ObjectInputStream(s.getInputStream());
			// 得到远程调用参数,包含了接口名,调用方法,方法参数
			RpcObject rpcObject = (RpcObject) is.readObject();
			// 构建接口的实现类,然后通过反射调用方法
			Object o = getObject(rpcObject.getC());
			Object reO = executeMethod(o, rpcObject.getMethodName(), rpcObject.getArgs());
			
			// 输出返回值
			os = new ObjectOutputStream(s.getOutputStream());
			os.writeObject(reO);
			os.flush();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
				os.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 通过反射技术执行方法,并返回返回值
	 * @param o
	 * @param methodName
	 * @param args
	 * @return
	 */
	private Object executeMethod(Object o, String methodName, Object[] args) {
		Object objR = null;
		Class[] cs = new Class[args.length];
		for (int i = 0; i < args.length; i++) {
			Object arg = args[i];
			cs[i] = arg.getClass();
		}
		try {
			Method m = o.getClass().getMethod(methodName, cs);
			objR = m.invoke(o, args);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return objR;
	}
	
	/**
	 * 根据接口名得到实例
	 * @param c
	 * @return
	 */
	private Object getObject(Class c) {
		Object o = null;
		try {
<span style="white-space:pre">			</span>o = ConfMonitor.conf.get(c.getName()).newInstance();
<span style="white-space:pre">		</span>} catch (InstantiationException e) {
<span style="white-space:pre">			</span>e.printStackTrace();
<span style="white-space:pre">		</span>} catch (IllegalAccessException e) {
<span style="white-space:pre">			</span>e.printStackTrace();
<span style="white-space:pre">		</span>}
		return o;
	}
}
服务器端配置文件,主要是配置接口对应了哪个实现类:

/**
 * 模拟配置,实际的框架中大部分都是使用xml进行配置的,比如Hessian是配置在web.xml的servlet属性里的
 * @author cdwangzijian
 *
 */
public class ConfMonitor {
	public static Map<String, Class> conf = new HashMap<String, Class>();
	
	static {
		conf.put("com.prince.rpc.service.IHello", HelloImpl.class);
	}
}

客户端

使用动态代理,给每一个方法调用都变为远程调用。

public class ProxyFactory {

	public static <T> T create(Class<T> c, String ip, int port) {
		 InvocationHandler handler = new RpcProxy(ip, port, c);
		
		return (T) Proxy.newProxyInstance(c.getClassLoader(),
                new Class[] {c },
                handler);
	}
}

代理将会封装远程参数,把调用的接口,方法名,参数传给远程,并且获得返回值

/**
 * 客户端接口代理
 * 当客户端接口方法被调用的时候,把方法名,方法参数作为参数。
 * 传送给远程服务执行,然后获取返回值
 * @author cdwangzijian
 *
 */
public class RpcProxy implements InvocationHandler, Serializable{
	private String ip;
	private int port;
	private Class c;
	
	private static final long serialVersionUID = 1L;

	public RpcProxy(String ip, int port, Class c) {
		this.ip = ip;
		this.port = port;
		this.c = c;
	}
	
	/**
	 * 动态代理类,当调用接口方法的时候转为调用此方法
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object o = null;		// 用作返回值
		// 通过socket调用远程服务
		Socket s = new Socket(ip, port);
		// 组装为一个保留了要调用的类,方法名及参数的对象,然后序列化之后传给远程
		RpcObject rpcObject = new RpcObject(c, method.getName(), args);
		ObjectOutputStream os = null;
		ObjectInputStream is = null;
		try{
			os = new ObjectOutputStream(s.getOutputStream());
			os.writeObject(rpcObject);
			os.flush();
			// 从远程得到返回结果
			is = new ObjectInputStream(s.getInputStream());
			o = is.readObject();
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			os.close();
			is.close();
		}
		
		return o;
	}
	
}
最后是调用部分,获得代理类。

public class RpcClient {
	public static void main(String[] args) {
		String ip = "localhost";
		int port = 9001;
		IHello hello = ProxyFactory.create(IHello.class, ip, port);
		System.out.println(hello.sayHello("wzj"));
	}
}

下面是用作测试的接口及实现类的代码:

public interface IHello {
	String sayHello(String name);
}

public class HelloImpl implements IHello {
	@Override
	public String sayHello(String name) {
		return "hello:" + name;
	}
}

最后就是源代码的下载路径了:  

http://download.csdn.net/detail/three_man/8059871


相关文章
|
2月前
|
负载均衡 Dubbo Java
Dubbo 3.x:探索阿里巴巴的开源RPC框架新技术
随着微服务架构的兴起,远程过程调用(RPC)框架成为了关键组件。Dubbo,作为阿里巴巴的开源RPC框架,已经演进到了3.x版本,带来了许多新特性和技术改进。本文将探讨Dubbo 3.x中的一些最新技术,包括服务注册与发现、负载均衡、服务治理等,并通过代码示例展示其使用方式。
73 9
|
7月前
|
消息中间件 负载均衡 Dubbo
如何自己设计一个类似Dubbo的RPC框架?
如何自己设计一个类似Dubbo的RPC框架?
60 0
|
4月前
|
Dubbo Java 应用服务中间件
Rpc编程系列文章第三篇:Hessian RPC一个老的RPC框架
Rpc编程系列文章第三篇:Hessian RPC一个老的RPC框架
|
2月前
|
XML JSON Java
RPC框架之Thrift—实现Go和Java远程过程调用
RPC框架之Thrift—实现Go和Java远程过程调用
46 1
|
3月前
|
消息中间件 Dubbo Java
Simple RPC - 01 框架原理及总体架构初探
Simple RPC - 01 框架原理及总体架构初探
51 0
|
9月前
|
Dubbo Java 应用服务中间件
Netty入门到超神系列-手撸简单版RPC框架(仿Dubbo)
原理还是比较简单 : 代理 + 线程池 + Netty 下面做一些解释: 首先需要定义一个统一的API接口,例:UserApi , 服务端(provider)需要实现这个接口,提供相应的方法UserApiImpl#save,客户端通过远程来调用该接口。 然后需要约定一个协议,服务器如何才能识别到客户端要调用哪个接口?:我这里用 “接口权限定名#方法名#参数” ,的方式来,因为是一个简单版本的RPC。服务端解析该内容就能匹配对应的接口的实现类,然后调用该方法。并把方法的返回值通过Netty写回给客户端 使用的编解码器都是比价简单的String的编解码器
112 0
|
9月前
|
前端开发
Netty手写RPC框架
创建Request类,继承Message,klass是调用的Class目标,name,parameterType,argument分别是方法名称,参数类型,参数
62 0
|
6月前
|
负载均衡 Dubbo 网络协议
微服务RPC框架:Feign和Dubbo
微服务RPC框架:Feign和Dubbo
287 0
|
6月前
|
JSON 中间件 Go
Go语言学习 - RPC篇:gin框架的基础能力剖析
gin是非常流行的一款HTTP框架。相较于原生的HTTP server,gin有很多改进点,主要在于3点: 1. 上手简单,开发思路与原生HTTP基本一致 2. 引入多个工具库,提高了开发效率 3. 生态丰富,有许多开源的组件 围绕着gin框架,我们将展开今天的话题。
103 2
Go语言学习 - RPC篇:gin框架的基础能力剖析
|
6月前
|
XML Dubbo Java
Dubbo第一讲:从RPC到Dubbo框架详解
Dubbo第一讲:从RPC到Dubbo框架详解