模拟RMI远程方法调用

RMI(Remote Method Invocation)远程方法调用

客户端向服务器端发送一个“请求“,服务器端处理该请求并将结果返回给客户端。如果把请求看成是一个”本地方法“,在客户端执行该方法,而实质上,在服务器端存在该方法的”本体“。

在这里插入图片描述
如上图所示,客户端通过代理,获取”代理对象“,执行相关方法,实质上是向服务器发送请求,请求客户端原本要执行的方法。服务器端维持一个”方法池“,将”远程对象“存储在池子中,起着RMI中注册表的作用。客户端连接服务器,网络发送请求需要将方法名称,参数类型,参数的值等信息发送过去。服务器端必须能够建立服务器,不断侦听来自客户端的连接请求,接收客户端发来的对应那个“方法”的名称、参数类型、参数值,根据这些信息找到相关方法,反射机制执行,将执行结果作为响应返回给客户端。

方法池
方法池以接口名称为键,以RMIDefinition为值的Map,RMIDefinition类封装了被调用方法所在类的klass和object,代码片段如下:

public class RMIDefinition {
	private Class<?> klass;
	private Object object;

XML配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<RmiMapping>
	<mapping interface = "com.mec.rmi.test.IAppInterface" 
		class = "com.mec.rmi.test.AppInterfaceImpl">
	</mapping>
</RmiMapping>

RMIFactory扫描配置文件:

public class RMIFactory {
	private static final Map<String, RMIDefinition> rmiPool = 
			new HashMap<String, RMIDefinition>();
	
	public RMIFactory() {
	}
	
	public static void scanRmiMapping(String mappingPath) {
		new XMLParser() {			
			@Override
			public void dealElement(Element element, int index) {
				String interfaceName = element.getAttribute("interface");
				String className = element.getAttribute("class");
				
				try {
					Class<?> klass = Class.forName(className);
					Object object = klass.newInstance();
					
					RMIDefinition rmiDefinition = new RMIDefinition(klass, object);
					
					rmiPool.put(interfaceName, rmiDefinition);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}.parseTag(XMLParser.getDocument(mappingPath), "mapping");;
	}
	
	public static RMIDefinition getRmiDefinition(String interfaceName) {
		return rmiPool.get(interfaceName);
	}

}

RMI客户端
客户端主要对外提供获取代理对象的方法,方法名称和参数类型与参数值,json序列化发送给服务器端。

	public <T> T getProxy(Class<?> klass) {
		MecProxy mecProxy = new MecProxy();
		IMethodInvoker methodInvoker = new IMethodInvoker() {			
			@SuppressWarnings({ "hiding"})
			@Override
			public <T> T methodInvoke(Object object, Method method, Object[] args) throws ConnectServerFailureException {
				Socket socket = null;
				DataInputStream dis = null;
				DataOutputStream dos = null;
				
				try {
					socket = new Socket(rmiServerIp, rmiServerPort);
					dos = new DataOutputStream(socket.getOutputStream());

					dos.writeUTF(method.toString());
					dos.writeUTF(getArgs(args));
					
					dis = new DataInputStream(socket.getInputStream());
					String resultString = dis.readUTF();
					Type type = method.getGenericReturnType();
					
					return ArgumentMaker.gson.fromJson(resultString, type);
				} catch (UnknownHostException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					close(dis, dos, socket);
				}
				
				return null;
			}
		};
		mecProxy.setMethodInvoke(methodInvoker);
		
		return mecProxy.getProxy(klass);
	}
	private String getArgs(Object[] args) {
		if (args == null || args.length <= 0) {
			return "";
		}
		
		ArgumentMaker maker = new ArgumentMaker();
		for (int i = 0; i < args.length; i++) {
			maker.add("arg" + i, args[i]);
		}
		return maker.toString();
	}

ArgumentMaker的有关介绍点这里

RMI服务器
服务器负责侦听客户端的请求,侦听到一个客户端的请求之后开一个线程去处理这个请求,即找到这个方法并反射机制执行,将返回值会送给客户端。

public class RMIServer implements Runnable {
	private ServerSocket server;
	private int port;
	private volatile boolean goon;
	
	public RMIServer() {
	}

	public void setPort(int port) {
		this.port = port;
	}
	
	public void startup() {
		if (goon) {
			return;
		}
		
		try {
			server = new ServerSocket(port);
			this.goon = true;
			new Thread(this, "RMI Server").start();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	public void shutdown() {
		if (goon == false) {
			return;
		}
		
		close();
	}
	
	private void close() {
		this.goon = false;
		try {
			if (server != null && !server.isClosed()) {
				server.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			server = null;
		}
	}

	@Override
	public void run() {
		while (goon) {
			try {
				Socket socket = server.accept();
				new RMIService(socket);
			} catch (IOException e) {
				if (goon) {
					goon = false;
				}
			}
		}
	}

}
public class RMIService implements Runnable {
	private Socket socket;
	
	public RMIService() {
	}
	
	public RMIService(Socket socket) {
		this.socket = socket;
		new Thread(this, "RMI Service").start();
	}

	@Override
	public void run() {
		try {
			DataInputStream dis = new DataInputStream(socket.getInputStream());
			DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
			
			String methodString = dis.readUTF();
			String argsString = dis.readUTF();
			
			Object result = invokeMethod(methodString, argsString);
			dos.writeUTF(ArgumentMaker.gson.toJson(result));
			
			dis.close();
			dos.close();
			socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private Object invokeMethod(String methodString, String argsString) {
		Object result = null;
		MethodInformation methodInfo = new MethodInformation(methodString);
		String interfaceName = methodInfo.getClassName();
		RMIDefinition rmiDefinition = RMIFactory.getRmiDefinition(interfaceName);
		Class<?> klass = rmiDefinition.getKlass();
		Object object = rmiDefinition.getObject();
		
		try {
			Method method = methodInfo.getMethod(klass);
			result = method.invoke(object, getParaValues(method, argsString));
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}

	private Object[] getParaValues(Method method, String argsString) {
		Object[] paraValues = null;
		Parameter[] paras = method.getParameters();
		int paraCount = paras.length;
		if (paraCount <= 0) {
			return new Object[] {};
		}
		
		paraValues = new Object[paraCount];
		ArgumentMaker maker = new ArgumentMaker(argsString);
		for (int i = 0; i < paraCount; i++) {
			paraValues[i] = maker.getValue("arg" + i, paras[i].getParameterizedType());
		}
		
		return paraValues;
	}
	
}

方法信息
MethodInformation是一个将方法名称,方法所在类对应的接口名称和该方法的参数的类型名称,作为成员,并对服务器找到方法以及反射机制执行方法提供一些“对外接口”。

public class MethodInformation {
	private String className;
	private String methodName;
	private String[] paraTypeName;
	
	MethodInformation() {
	}
	
	MethodInformation(String methodString) {
		parse(methodString);
	}

	String getClassName() {
		return className;
	}

	String getMethodName() {
		return methodName;
	}

	String[] getParaTypeName() {
		return paraTypeName;
	}
	
	MethodInformation parse(String methodString) {
		String[] methodInfoString = methodString.split(" ");
		String methodInfo = methodInfoString[methodInfoString.length - 1];
		int leftBracketIndex = methodInfo.indexOf('(');
		String methodFullName = methodInfo.substring(0, leftBracketIndex);
		int lastDotIndex = methodFullName.lastIndexOf('.');
		this.className = methodFullName.substring(0, lastDotIndex);
		this.methodName = methodFullName.substring(lastDotIndex + 1);
		
		String paraTypes = methodInfo.substring(leftBracketIndex + 1, methodInfo.length() - 1);
		parseParaTypes(paraTypes);
		
		return this;
	}
	
	void parseParaTypes(String paraTypes) {
		if (paraTypes.length() <= 0) {
			paraTypeName = new String[] {};
			return;
		}
		paraTypeName = paraTypes.split(",");
	}
	
	Method getMethod(Class<?> klass) throws NoSuchMethodException, SecurityException {
		Method method = klass.getDeclaredMethod(methodName, getParaTypes(klass));
		return method;
	}
	
	Class<?>[] getParaTypes(Class<?> klass) {
		int paraCount = paraTypeName.length;
		if (paraCount <= 0) {
			return new Class<?>[] {};
		}
		Class<?>[] paraTypes = new Class<?>[paraCount];
		for (int i = 0; i < paraCount; i++) {
			try {
				Class<?> paraType = Class.forName(paraTypeName[i]);
				paraTypes[i] = paraType;
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		
		return paraTypes;
	}

}

以上模拟RMI技术的核心:
1.代理;
2.反射机制;
3.网络传递参数;
4.网络传递返回值;
5.json序列化
6.短链接

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值