rpc框架(java动态代理+socket编程)

本文详细介绍了一个基于Socket编程和Java动态代理的手动实现的RPC框架。从公共API接口定义到服务端和服务端的具体实现,深入解析了RPC的工作原理,包括如何通过Socket进行远程方法调用,以及如何利用动态代理实现客户端的远程调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近公司让跑一个基于Dubbo的 C/S 架构的项目,之前对于rpc的印象是基于rmi协议的,rmi帮我们屏蔽了底层通信的细节,让我们使用远程对象像使用本地对象一样(基于两个虚拟机之间的通信)。rmi和dubbo之后会会不断更新。这章主要说说rpc。

RPC

RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范,它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显示编码这个远程调用的细节,即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。
比如我本地要调用另外一台与我网络连通的机器上的一个方法,那么我需要先通过流的形式告诉那台机器,我要调用什么方法,然后那个机器上处理后通过流的形式返回到我这台机器上,然后我通过反序列化拿到返回的结果。这样就实现了基本的rpc过程。
本片文章主要参考了alibaba Dubbo框架作者 梁飞的技术文章:RPC框架几行代码就够了
因为先接触的动态代理,偶尔看到梁大的这篇文章,于是又专门去看了看Socket编程。结合自己的理解也手撸一个。
知识点储备:
  • Socket编程 (两台机器交互的基础)
  • java动态代理 (这个可能不太好理解,需要先去了解.java源文件是怎么加载到JVM中并生成class对象的过程,在这个上面我做了不少功课)
如果没有了解以上两块内容的道友,需要先去了解一下。(后续我会补充以上两块的内容)

废话不多说,上代码。

公共API接口
/**
 * 公共Api 接口
 * @author Qiu Ping
 */


public interface Hello {

	String hello(String name);

}

服务端Imp
/**
 * Server API IMP
 * @author Qiu Ping
 */

public class myimp implements Hello {
	@Override
	public String hello(String name) {
		System.out.println("我是服务端实现类方法: Hello "+name);
		return "You name is " + name;
	}
}

接口Server和Client都要引入,具体实现只需在Server端实现即可,客户端远程调用。

SocketServer

try块很多,不要着急,大部分我都写了注释,慢慢看。
/**
 * Single RpcFm Server
 * @author Qiu Ping
 * 知识点:Socket编程 IO流 反射
 */
public class SocketServer {

	/**
	 * 暴露服务
	 * @param port 服务端口
	 * @throws IOException
	 */

	public static void ServiceExport(Object serviceimp,int port) throws IOException{
		//判断
		if(serviceimp == null){
			throw new IllegalArgumentException("serviceimp is null !");
		}
		if(port<0 || port > 65536){
			throw new IllegalArgumentException("port is error !");
		}

		System.out.println(serviceimp.getClass().getName()+"is Export on port "+port);
		//监听端口
		ServerSocket serversocket = new ServerSocket(port);
		//还有一种写法 for(;;) 效果一样 死循环
		for(;;){
			//持续监听Client端,构建服务端Socket
			//通信是双方Socket通信,ServerSocket 提供了端口监听
			//final关键字 指定该Socket只能指向一个地址
			final Socket socket = serversocket.accept();
			//起线程,约束通信规则
			new Thread(new Runnable(){
				//重写Runnable接口run方法
				@Override
				public void run() {
					try {
						System.out.println("我是线程内方法!");
						//获取Client Object流
						ObjectInputStream objinp = new ObjectInputStream(socket.getInputStream());
						//以UTF8的编码读取调用方法名
						String methodname = objinp.readUTF();
						System.out.println("调用方法名为:"+methodname);
						try {
							//方法参数类型
							Class<?>[] parameterTypes = (Class<?>[])objinp.readObject();
							//方法参数
							Object[] arguments = (Object[])objinp.readObject();
							System.out.println("方法参数为:"+arguments[0].toString());
							try {
								//根据Client请求信息构造出Method对象
								Method method = serviceimp.getClass().getMethod(methodname, parameterTypes);
								//获取Output流,返回结果
								ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
								try {
									//执行远程方法
									Object result = method.invoke(serviceimp, arguments);
									System.out.println("我是执行结果:"+result.toString());
									output.writeObject(result);
									output.flush();
								} catch (IllegalAccessException | IllegalArgumentException
										| InvocationTargetException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								} finally {
									output.close();
								}
							} catch (NoSuchMethodException | SecurityException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						} catch (ClassNotFoundException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} finally {
							objinp.close();
						}
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}).start();

		}



	}


}

SocketClient

/**
 * Single RpcFm Client
 * @author Qiu Ping
 * 知识点:Socket IO流 动态代理
 * 正如梁大所说 Rpc框架核心就是 Socket编程+动态代理
 */



public class SocketClient {


	/**
	 * 服务调用
	 * @param interfaceClass 接口
	 * @param Host ServerIP地址
	 * @param port 服务端口
	 * @return
	 */
	public static Object invok(Class<?> interfaceClass,String Host,int port){
		if(interfaceClass == null){
			throw new IllegalArgumentException("Interface is null");
		}
		if(!interfaceClass.isInterface()){
			throw new IllegalArgumentException("The " + interfaceClass.getName() + " not a interface !");
		}
		if (Host == null || Host.length() == 0){
			throw new IllegalArgumentException("Host == null!");
		}
		if (port <= 0 || port > 65535)  {
			throw new IllegalArgumentException("Invalid port " + port);
		}
		System.out.println("Get service from "+Host+":"+port+" from server !");

		//核心的东西来了啊,动态代理
		return Proxy.newProxyInstance(
				interfaceClass.getClassLoader(),
				new Class<?>[] {interfaceClass},
				new InvocationHandler(){
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("客户端开始调用!");
						//SocketClient
						Socket socket = new Socket(Host, port);
						//获取输出流
						ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
						//按照双方约束规则写入数据
						//1.方法名称
						output.writeUTF(method.getName());
						//2.参数类型
						output.writeObject(method.getParameterTypes());
						//3.参数
						output.writeObject(args);
						//接收流
						ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
						Object result = input.readObject();
						input.close();
						socket.close();
						return result;
					}
				});


	}

}

编码工作基本结束,接下来测试.

Server port:8001 -------- Client port:8002

在这里插入图片描述
服务端已经启动,等到客户端简历socket连接。
在这里插入图片描述
查看客户端打印出调用服务端方法后成功返回执行结果。

后续会补上相关知识点内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值