关于JAVA的笔记(RPC:远程服务调用)

RPC:Remote Procudure Call
简单来说就是客户端远程调用服务端的方法
设计为三个角色分别为服务器端接口,服务注册中心(包含所有接口),客户端。
所需要

  1. socket链接服务器与客户端
  2. 注册中心包含所有接口的值
  3. 服务器通过反射解析信息
  4. 服务端返回接口,通过动态代理
服务器端接口
  • 接口
package server;

public interface HelloService {
void sayHi(String name);
}
  • 实现类
package server;

public class HelloServiceImpl implements HelloService {
	public void sayHi(String name) {
		// TODO Auto-generated method stub
System.out.println("hello"+name);
	}
}
服务器中心接口
package server;
public interface Server {
void start()throws Exception;
void stop();
void register(Class<?> service,Class<?> serviceImpl);
}

start开启服务
stop停止服务
register注册接口入MAP中

服务器中心接口实现
//map 注册接口到MAP
	private static HashMap<String, Class> serviceRegiser=new HashMap<>();

设置一个Hashmap 其KEY值为接口名 value值为接口对象

public void register(Class<?> service,Class<?> serviceImpl) {
		serviceRegiser.put(service.getName(), serviceImpl);
		// TODO Auto-generated method stub
	}

将接口注册入map中,方便起见直接使用getName设置KEY值

private static final Integer PORT=9999;
public void start()   {
		// TODO Auto-generated method stub
		ServerSocket servers=null;
		
		ObjectOutputStream output=null;
		ObjectInputStream input=null;

设置固定端口号
ServerSocket准备接收链接
ObjectOutputStream output=null;ObjectInputStream input=null;
为方便使用本对象进行数据交换

servers = new ServerSocket();
			servers.bind(new InetSocketAddress(PORT));
			Socket socket=servers.accept();
			//接收到客户端链接请求
			 input=new ObjectInputStream(socket.getInputStream());

接收对象并接受客户端传参

 input=new ObjectInputStream(socket.getInputStream());
			//顺序接收
			String intername=input.readUTF();
			String methodname=input.readUTF();
			Class<?>[] methodparatype=(Class[])input.readObject();
			Object[] parameters=(Object[])input.readObject();

按顺序分别为接口名,对应方法,参数列表,参数

//开始找接口
			Class<?> SeviceClass=serviceRegiser.get(intername);
			Method metod=SeviceClass.getMethod(methodname, methodparatype);
			Object resObject = metod.invoke(SeviceClass.newInstance(), parameters);

获取接口,通过方法名获取方法,将参数及列表调用方法获得返回值

 output=new ObjectOutputStream(socket.getOutputStream());
			output.writeObject(resObject);

将返回值返回客户端

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

关闭资源

客户端
//获取代表服务端接口的动态代理对象
	//serviceName接口名 
	@SuppressWarnings("unchecked")
	public static <T> T getRemoteProxyObject(Class serviceNameInterface,InetSocketAddress in) {
		/*
		 * newProxyInstance(a,b,c)
		 * a:类加载器 :代理类的类加载器 单独String不可以获得 因此使用Class类型获得其类加载器
		 * b:代理对象具备方法 接口 多接口传入数组
		 * c:handler 对象
			 */
		 		return (T)Proxy.newProxyInstance(serviceNameInterface.getClassLoader(), new Class<?>[] {serviceNameInterface}, new InvocationHandler() {
			/*
			 * proxy:代理对象 method方法 args(方法参数列表)
			 */

采用动态代理方式来调用

 * proxy:代理对象 method方法 args(方法参数列表)
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) {
				// TODO Auto-generated method stub

重写handler对象

ObjectOutputStream output=null;
				ObjectInputStream input=null;

与服务器端一样为方便使用本对象进行数据交换

Socket 	 socket=new Socket();
										socket.connect(in);
					 output=new ObjectOutputStream(socket.getOutputStream()) ;//发送序列化

将对应参数发送至服务器

//发送接口名方法名
					output.writeUTF(serviceNameInterface.getName());
					output.writeUTF(method.getName());
					//方法参数类型 方法参数 
					output.writeObject(method.getParameterTypes());
					output.writeObject(args);
					//等待服务端返回

按顺序写入流

input=new ObjectInputStream(socket.getInputStream());
			return input.readObject();

接受返回流并返回 例行关闭资源就不写了

后续优化

  1. 保持开启并循环监听
    外置
	ServerSocket servers=servers = new ServerSocket();
		servers.bind(new InetSocketAddress(PORT));

循环

while(true){
....
//仅仅如此会造成阻塞
}

通过多线程来实现并发解决阻塞

	private static ExecutorService executor=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

其中Runtime.getRuntime().availableProcessors()获取处理器数量来初始化线程池容量。

private static class ServiceTask implements Runnable{
		ObjectOutputStream output=null;
		ObjectInputStream input=null;
		Socket socket=null;
		public ServiceTask(Socket socket) {
			this.socket=socket;
		}
		@Override
		public void run() {try {
		
		//接收到客户端链接请求
		 input=new ObjectInputStream(socket.getInputStream());
		//顺序接收
		String intername=input.readUTF();
		String methodname=input.readUTF();
		Class<?>[] methodparatype=(Class[])input.readObject();
		Object[] parameters=(Object[])input.readObject();
		//开始找接口
		Class<?> SeviceClass=serviceRegiser.get(intername);
		Method metod=SeviceClass.getMethod(methodname, methodparatype);
		Object resObject = metod.invoke(SeviceClass.newInstance(), parameters);
		//返回客户端
		 output=new ObjectOutputStream(socket.getOutputStream());
		output.writeObject(resObject);
	} catch (ClassNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (NoSuchMethodException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (SecurityException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (IllegalArgumentException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (InvocationTargetException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (InstantiationException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}finally {
		try {
			if(output!=null)
			output.close();
			if(input!=null)
				input.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
					}}
		}
	// TODO Auto-generated method stub
	}

将执行操作转移入内部实现Runable方法中实现run方法。并将socket作为参数传入该对象。
修改后while中代码块为

while(true) {
				Socket socket=null;
			try {
				socket = servers.accept();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			executor.execute(new ServiceTask(socket));
			}
			

设置状态量判断服务是否开启

private static boolean isRunning=false;
。。。。。。
isRunning=true;

stop方法用来关闭服务

	public void stop() {
		// TODO Auto-generated method stub
isRunning=false;
executor.shutdown();
	}

设置状态量为关闭,并关闭线程池

测试

服务器端 并且通过线程启动

package server;

public class TEst {
public static void main(String[] args) {
	new Thread(new Runnable() {
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			Server server=new ServerCenter();
			server.register(HelloService.class, HelloServiceImpl.class);
			try {
				server.start();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}).start();
	};
}


客户端

package client;
import java.net.InetSocketAddress;
import server.HelloService;
public class Ctest {
public static void main(String[] args) throws ClassNotFoundException {
	HelloService service=Client.getRemoteProxyObject(Class.forName("server.HelloService"), new InetSocketAddress("127.0.0.1",9999));
	System.out.println(service.sayHi("aa"));
}
}

整体思路:
rpc:

  1. 客户端通过socket请求服务器端,并且通过字符串形式将需要请求的接口的发送给服务端
  2. 服务端将可以提供的接口注册到服务中心
  3. 服务端接受请求,在服务中心找对应实现类
  4. 返回至客户端
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值