RMI简单实现

本文详细解析了RPC(远程过程调用)的工作原理,包括客户端如何通过代理调用服务器端方法,以及服务器端如何处理请求并执行相应方法。同时,文章提供了具体的代码实现,展示了客户端与服务器端的交互过程。

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

  • 背景

RMI(RPC),是一种远程过程调用。这里对其概念就不赘述了,简单说下个人理解。远程调用,便是在客户机端远程调用服务器端的方法,并将结果返回给客户端。这样的话,客户端无需关心方法的具体实现,只需注意调用的参数等。

  • 实现思路

1.其实这里可以分开思考,最外面的框架无非是,客户端通过代理去执行方法。这也是rpc给人最浅层的感受。就感觉方法只是在本地执行一样。

2.然后再加入网络传输的概念,代理机制里实质上将方法和参数通过网络传给SERVER。

3.server监听到一个客户端连接请求时,交给Executor去处理。

4.Executor通过beanFactory取得相关的方法,再通过反射机制执行。

5.将结果再传回client便结束了。

当然上面的过程只是执行方法的过程,因为执行前server还需要factory的初始化和注册Bean的注册过程。而这些过程,还是在后面结合代码作以叙述吧。

  • 代码实现

这里先给出RPCClient的代码。可以看到,client层没有干什么事,只给给出代理,并将执行的事物先交给Executor(实际执行者)去处理。这里要注意一点,为了避免调用method的重载方法,这里需要将method作如下的操作,因为重载方法的hashcode()是相同的。

public class RpcClient {
	private RpcClientExecutor rpcClientExecutor;
	
	public RpcClient() {
	}
	
	public RpcClient(String serverIp, int serverPort) {
		this.rpcClientExecutor = new RpcClientExecutor(serverIp, serverPort);
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getProxy(Class<?> klass) {
		return (T) Proxy.newProxyInstance(
				klass.getClassLoader(),
				new Class<?>[] { klass },
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						String rpcBeanId = String.valueOf(method.toString().hashCode());
						return rpcClientExecutor.rpcExecutor(rpcBeanId, args);
					}
				});
	}
	
}

client层将所有事情甩手交给Executor去处理,由它去负责传输方法执行的参数和beanId.并负责接受有服务器执行的结果。这里也有一点需要注意,那就是inputStream和OutInputStream的执行顺序,因为这里没有用线程去接收,因此必须这么做。

public class RpcClientExecutor {
	private int serverPort;
	private String serverIp;
	
	public RpcClientExecutor() {
	}
	
	public RpcClientExecutor(String serverIp, int serverPort) {
		this.serverIp = serverIp;
		this.serverPort = serverPort;
	}

	protected int getServerPort() {
		return serverPort;
	}

	protected void setServerPort(int serverPort) {
		this.serverPort = serverPort;
	}

	protected String getServerIp() {
		return serverIp;
	}

	protected void setServerIp(String serverIp) {
		this.serverIp = serverIp;
	}
	
	private void closeSocket(ObjectInputStream ois, ObjectOutputStream oos, Socket socket) {
		try {
			if(ois != null) {
				ois.close();
			} 
		}	catch (IOException e) {
			e.printStackTrace();
		}finally {
			ois = null;
		}
		try {
			if(oos != null) {
				oos.close();
			} 
		}	catch (IOException e) {
			e.printStackTrace();
		}finally {
			oos = null;
		}
		try {
			if(socket != null && !socket.isClosed()) {
				socket.close();
			} 
		}	catch (IOException e) {
			e.printStackTrace();
		}finally {
			socket = null;
		}
	}
	
	@SuppressWarnings("unchecked")
	<T> T rpcExecutor(String rpcBeanId, Object[] agrs) throws Exception {
		Socket socket = new Socket(serverIp, serverPort);
		ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
		
		oos.writeUTF(rpcBeanId);
		oos.writeObject(agrs);
		
		ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
		Object result = ois.readObject();
		
		closeSocket(ois, oos, socket);
		return (T)result;
	}
}

这时字节码便会传输到server层,而在server层,功能也是很单调的,每次server接收到一个client连接,便产生一个executor去处理。不影响server层的继续侦听。

public class RpcServer implements Runnable {
	private ServerSocket server;
	private int port;
	private Boolean goon;
	private RpcBeanFactory beanFactory;
	private long executorId;
	
	public RpcServer() {
		beanFactory = new RpcBeanFactory();
		goon = false;
	}
	
	public void setPort(int port) {
		this.port = port;
	}
	
	RpcBeanFactory getRpcBeanFactory() {
		return beanFactory;
	}
	
	public void startRpcServer() throws IOException {
		if(this.port == 0) {
			return;
		}
		this.server = new ServerSocket(port);
		this.goon = true;
		new Thread(this, "RPC_SERVER").start();
	}
	
	public void stopServer() {
		if(this.server != null && !this.server.isClosed()) {
			try {
				this.server.close();
			} catch (IOException e) {
				e.printStackTrace();
			}finally {
				this.server = null;
			}
		}
	}
	
	@Override
	public void run() {
		while(goon) {
			try {
				Socket client = server.accept();
				new RpcServerExecutor(this, client, ++executorId);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void registryRpc(Class<?> interfaces, Object object) {
		RpcBeanRegisty.registyInterface(beanFactory, interfaces, object);
	}

	public void registryRpc(Class<?> interfaces, Class<?> implementsClass) {
		RpcBeanRegisty.registyInterface(beanFactory, interfaces, implementsClass);
	}
}

你会发现,客户端所要执行的方法,其实是在这里执行的。但就存在一个问题,客户端传过来的参数是远远不够执行的。其实,server这里是有一个beanfactory的。它负责存储server层所有的bean,简单说,就是可以被远程调用的所有的方法。

public class RpcServerExecutor implements Runnable{
	private Socket socket;
	private RpcServer server;
	private ObjectInputStream ois;
	private ObjectOutputStream oos;
	
	public RpcServerExecutor(RpcServer server, Socket socket, long executorId) throws IOException {
		this.server = server;
		this.socket = socket;
		this.ois = new ObjectInputStream(socket.getInputStream());
		this.oos = new ObjectOutputStream(socket.getOutputStream());
		new Thread(this, "RPC_EXECUTOR_"+ executorId).start();
	}
	
	private void closeSocket() {
		try {
			if(this.ois != null) {
				this.ois.close();
			}
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			this.ois = null;
		}
		try {
			if(this.oos != null) {
				this.oos.close();
			}
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			this.oos = null;
		}
		try {
			if(this.socket != null && !this.socket.isClosed()) {
				this.socket.close();
			}
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			this.socket = null;
		}	
	}
	
	@Override
	public void run() {
			try {
				String beanId = ois.readUTF();
				Object[] args = (Object[]) ois.readObject();
				showParameters(args);
				BeanDefination beanDefination = server.getRpcBeanFactory().getRpcBean(beanId);
				Method method = beanDefination.getMethod();
				Object object = beanDefination.getObject();
				Object result = method.invoke(object, args);
				
				oos.writeObject(result);
			} catch (Exception e) {
				e.printStackTrace();
			}finally {
				closeSocket();
			}
			
	}

	private void showParameters(Object[] args) {
		for(int i = 0; i < args.length; i++) {
			System.out.println("第" + i + "个"+ args[i]);
		}
	}
}

因此Beanfactory的存在意义就不用赘述了

public class RpcBeanFactory {
	private final Map<String, BeanDefination> rpcBeanMap;
	
	public RpcBeanFactory() {
		rpcBeanMap = new HashMap<>();
	}
	
	public void beanRegisty(String beanId, BeanDefination beanDefination) {
		if(rpcBeanMap.containsKey(beanId)) {
			return;
		}
		
		rpcBeanMap.put(beanId, beanDefination);
	}
	
	public BeanDefination getRpcBean(String beanId) {
		return rpcBeanMap.get(beanId);
	}
}
public class BeanDefination {
	private Class<?> klass;
	private Method method;
	private Object object;
	
	public BeanDefination() {
	}
	
	protected Class<?> getKlass() {
		return klass;
	}
	
	protected void setKlass(Class<?> klass) {
		this.klass = klass;
	}
	
	protected Method getMethod() {
		return method;
	}
	
	protected void setMethod(Method method) {
		this.method = method;
	}
	
	protected Object getObject() {
		return object;
	}
	
	protected void setObject(Object object) {
		this.object = object;
	}
}

可以看到,beanfactory是最基本的单个方法的注入,这是最底层的。这里就的多说点了。我们代理机制用的是JDK代理。也就是说我们在client能只有一个接口,client只知道这个接口中的方法。它可以远程调用接口中的所有的方法。因此服务器这里必须能对接口进行注册。并且有一点,将来的执行方法的对象是在服务器层产生,因此,还需要在注册时,给出接口的实现类,方便之后的实例化。

public class RpcBeanRegisty {
	
	public RpcBeanRegisty() {
	}
	
	private static void doRegisty(RpcBeanFactory beanFactory, Class<?> interfaces, Object object) {
		Method[] methods = interfaces.getDeclaredMethods();
		for(Method method : methods) {
			String beanId = String.valueOf(method.toString().hashCode());
			BeanDefination beanDefination = new BeanDefination();
			beanDefination.setKlass(interfaces);
			beanDefination.setMethod(method);
			beanDefination.setObject(object);
			
			beanFactory.beanRegisty(beanId, beanDefination);
		}
	}
	
	public static void registyInterface(RpcBeanFactory beanFactory, Class<?> interfaces, Object object ) {
		if(!interfaces.isAssignableFrom(object.getClass())) {
			return;
		}
		doRegisty(beanFactory, interfaces, object);
	}
	
	public static void registyInterface(RpcBeanFactory beanFactory, Class<?> interfaces, Class<?> klass) {
		if(!interfaces.isAssignableFrom(klass)) {
			return;
		}
		
		try {
			doRegisty(beanFactory, interfaces, klass.newInstance());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

这里我们可以发现,这个注册类,其实只是一个工具,因此我们给的方法都是static的,不需要产生对象,即可直接使用。

  • 总结

rpc终于是写完了。其实最近的小项目上,发现代理机制真的很犀利。好多框架的实现,必须用到代理机制,才能谈及之后的设计。亦可以说,它是比较基本的,底层的东西。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值