RPC(Remote Procedure Call)远程过程调用应用总结

RPC概念

RPC是远程过程调用(Remote Procedure Call)的缩写形式。第三方的客户程序通过接口调用服务器内部的标准或自定义函数,获得函数返回的数据进行处理。

代码设计思想
  • 客户端:通过动态代理,将要访问的接口名字、接口中方法的名字、方法中的参数类型和方法中的参数,通过Socket以序列化流(对象流)的方式发送至服务器端或接收服务器端的返回信息。
  • 服务端:服务端利用ServerSocket将客户端发送的数据进行接收,并将发送过来的方法名保存在Map中,其中map中的Key=方法名的,Value=方法的实现类的Class类,通过反射将服务器端的“真正要调用的方法”执行,并将返回值发送至客户端。
设计流程图

在这里插入图片描述

代码演示
  • 调用接口
package indi.dsl.rpcdome;

public interface HelloClient {
	public String sayHi(String name);

}

  • 调用接口实现
package indi.dsl.rpcdome;

public class HelloClientImpl implements HelloClient{

	@Override
	public String sayHi(String name) {
		
		return "Hi,"+name;
	}


}

  • 服务器中服务中心接口
package indi.dsl.rpcdome.server;

public interface ServerCenter {
	public void start() throws Exception;
	public void stop();
	public void register(Class server,Class serverImpl);

}

  • 服务器中服务中心接口实例
package indi.dsl.rpcdome.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ServerCenterImpl implements ServerCenter {
	private static HashMap<String, Class> serverRegister = new HashMap<>();
	private static int port;// 端口号
	private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
	private static boolean isRuning = false;

	public ServerCenterImpl(int port) {
		this.port = port;

	}

	// 开启服务端服务
	@Override
	public void start() throws Exception {
		ServerSocket server = null;
		try {
			server = new ServerSocket();
			// 绑定IP和端口
			server.bind(new InetSocketAddress(port));
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

		// 服务已经启动
		isRuning = true;
		while (true) {
			System.out.println("Server Start.....");
			Socket socket = null;
			try {
				socket = server.accept();
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}

			// 启动线程
			executor.execute(new ServerTask(socket));

		}

	}

	@Override
	public void stop() {
		// 关闭服务
		isRuning = false;
		executor.shutdown();

	}

	@Override
	public void register(Class server, Class serverImpl) {
		// 将接口名和接口的实现,以KEY,VALUE的形式存储注册
		serverRegister.put(server.getName(), serverImpl);

	}

	private static class ServerTask implements Runnable {
		// 因为使同一次客户请求,所以实际调用的socket和线程中的socket一致,
		// 将new ServerTask(socket)中传入的socket利用有参构造函数进行传入保持统一
		private Socket socket;

		public ServerTask() {

		}

		public ServerTask(Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			ObjectInputStream input = null;
			ObjectOutputStream output = null;
			try {

				// 接受客户端请求数据,并处理该请求数据
				input = new ObjectInputStream(socket.getInputStream());
				// 因为使用ObjectInputStream对象流(序列化流)接受数据数据有严格的顺序要求,发送顺序和接收顺序应该严格对应
				// 接收接口名
				String serverName = input.readUTF();
				// 接收方法名
				String methodName = input.readUTF();
				// 接收方法参数类型
				Class[] parameterTypes = (Class[]) input.readObject();
				// 接收参数
				Object[] arguments = (Object[]) input.readObject();
				System.out.println("run...");
				System.out.println("serverName:" + serverName);
				System.out.println("methodName:" + methodName);
				System.out.println("parameterTypes:" + parameterTypes);
				System.out.println("arguments:" + arguments);
				// 根据客户请求,到map中找到与之对应的具体接口
				Class serverClass = serverRegister.get(serverName);
				// 通过接口对象serverClass和传入的方法名,找到接口中的与之对应的方法
				Method method = serverClass.getMethod(methodName, parameterTypes);
				// 调用相应方法,使用invoke执行该方法
				Object result = method.invoke(serverClass.newInstance(), arguments);

				// 将调用方法处理的结果返回给客户端
				output = new ObjectOutputStream(socket.getOutputStream());
				output.writeObject(result);

			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			} finally {

				try {
					if (output != null)
						output.close();
					if (input != null)
						input.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}

		}

	}

}

  • 客户端代码
package indi.dsl.rpcdome.client;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;

public class Client {
	// 获取代表服务端接口的动态代理对象(HelloClent)
	@SuppressWarnings("unchecked")
	// serverInterface : 请求的接口名称
	// adr:服务端的IP和端口
	public static <T> T getRemoteProxyObj(Class serverInterface, InetSocketAddress adr) {
		return (T) Proxy.newProxyInstance(serverInterface.getClassLoader(), new Class<?>[] { serverInterface },
				new InvocationHandler() {
					// Object proxy:要代理的对象,
					// Method method:代理对象的方法名
					// Object[] args:代理对象的参数数组
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						// 客户端向服务端发送数据
						Socket socket = new Socket();
						ObjectOutputStream output = null;
						ObjectInputStream input = null;
						
						try {
							// 通过IP和端口连接服务器
							socket.connect(adr);
							output = new ObjectOutputStream(socket.getOutputStream());
							// 发送接口名和方法名
							output.writeUTF(serverInterface.getName());
							output.writeUTF(method.getName());
							// 发送参数类型和参数
							output.writeObject(method.getParameterTypes());
							output.writeObject(args);
							// 等待服务端处理
							// 接收服务端返回结果
							input = new ObjectInputStream(socket.getInputStream());
							// 将服务端结果返回
							return input.readObject();
						} catch (Exception e) {
							e.printStackTrace();
							return null;
						}finally {
							if(input!=null)input.close();
							if(output!=null)output.close();
						}
					}
				});
	}

}

  • 服务器端测试代码:
package indi.dsl.rpcdome.test;



import indi.dsl.rpcdome.HelloClient;
import indi.dsl.rpcdome.HelloClientImpl;
import indi.dsl.rpcdome.server.ServerCenter;
import indi.dsl.rpcdome.server.ServerCenterImpl;

public class RPCDomeTest {
	public static void main(String[] args) {
		// 优化:利用线程开启服务端
		new Thread(new Runnable() {

			@Override
			public void run() {
			 ServerCenter server = null;
				try {
					server = new ServerCenterImpl(9999);
					server.register(HelloClient.class, HelloClientImpl.class);
					server.start();
					
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}
		}).start();

	}

}

  • 客户端测试代码:
package indi.dsl.rpcdome.test;

import java.net.InetSocketAddress;

import indi.dsl.rpcdome.HelloClient;
import indi.dsl.rpcdome.client.Client;

public class ClientTest {
	public static void main(String[] args) throws ClassNotFoundException {
		HelloClient client = Client.getRemoteProxyObj(Class.forName("indi.dsl.rpcdome.HelloClient"),
				new InetSocketAddress("127.0.0.1", 9999));
		System.out.println(client.sayHi("zs"));
	}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值