面试官不讲码德,欺负我一个年轻的开发工程师,问如果是你怎么设计RPC?
RPC也不是很难啊,教你如何使用socket加动态代理与反射实现Rpc
先来解释解释一下rpc,首先很多人以为rpc是一种协议,其实这个就是出错误的,rpc:是远程过程调用;
看他的全程英文RemotePositionControl他其实是一种设计思想而已,解决分布式各个系统之间的调用关系。
我们今天就用socket方式实现一套rpc调用框架,不多说上代码
Java从入门到项目实战java语言编程入门书零基础自学教程淘宝¥49.8¥79.8购买packagerpc.socket;
//先定义一个clinet接口
publicinterfaceClinetT{
TgetService(ClassTtClass);
}
这是我个人写的一个实现类,给位大牛可以尝试实现
这里是采用动态代理。把调用大的过程交给代理对象,这样就可以屏蔽掉底层的网络和整个调用过程,
对于客服而言只用给一个接口的class对象,他会帮你去找到服务端实现类,实现远程调用
importjava.io.*;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
importjava.net.Socket;
importjava.util.Properties;
publicclassRpcClintimplementsClinet{
finalstaticStringRPC_SERVER_HOST=RPC_SERVER_HOST;
finalstaticStringRPC_SERVER_PORT=RPC_SERVER_PORT;
OverridepublicObjectgetService(ClassaClass){
returnProxy.newProxyInstance(aClass.getClassLoader(),newClass[]{aClass},newInvocationHandler(){
OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
//读取配置文件
Propertiesproperties=newProperties();
InputStreamresourceAsStream=aClass.getClassLoader().getResourceAsStream(rpc/RpcConfig.properties);
properties.load(resourceAsStream);
Stringhost=properties.getProperty(RPC_SERVER_HOST);
Integerport=newInteger((String)properties.get(RPC_SERVER_PORT));
//RPC注册过程
Socketsocket=newSocket(host,port);
OutputStreamin=socket.getOutputStream();
ObjectOutputStreamobjectOutputStream=newObjectOutputStream(in);
//告诉服务端调用的是哪个类
objectOutputStream.writeObject(aClass.getName());
//告诉服务端调用的是哪个方法
objectOutputStream.writeObject(method.getName());
//告诉服务端调用方法传入的参数
objectOutputStream.writeObject(args);
//告诉服务端调用方法的参数类型
objectOutputStream.writeObject(method.getParameterTypes());
//完成序列化,刷新
objectOutputStream.flush();
//接受服务端响应结果
ObjectInputStreamreturnObj=newObjectInputStream(socket.getInputStream());
Objecto=returnObj.readObject();
//关闭流
objectOutputStream.close();
returnObj.close();
resourceAsStream.close();
socket.close();
returno;
}
});
}
这是服务端接口
importjava.io.IOException;
publicinterfaceServer{
voidHandler()throwsIOException;
这是我实现的服务端rpc,就是采用一个nio接受客服端数据,然后去调用服务端的方法
importrpc.socket.service.GODService;
importrpc.socket.service.GODServiceImpl;
importjava.io.ObjectInputStream;
importjava.io.ObjectOutputStream;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.InvocationTargetException;
importjava.net.ServerSocket;
importjava.util.Map;
importjava.util.concurrent.ConcurrentHashMap;
importjava.util.concurrent.ConcurrentMap;
publicclassRpcServerimplementsServer{
//装用实现类的bena
staticMapString,ClassRESmap;
//加载所有需要注册的额服务
static{
//这里可以用Aop加注解的方式来把需要暴露的服务,注册到服务列表里面去
RESmap=newConcurrentHashMap();
//把服务注册到注册表
RESmap.put(GODService.class.getName(),GODServiceImpl.class);
publicvoidHandler()throwsIOException{
ServerSocketserverSocket=newServerSocket();
//采取传统的bio处理
while(true){
//等待客服端链接
Socketaccept=serverSocket.accept();
newThread(()-{
try{
//等待客户端输入
ObjectInputStreamin=newObjectInputStream(accept.getInputStream());
//获取客户端传过来的类名称
StringclassName=(String)in.readObject();
//获取客户端客服端传过来的方法名称
StringmethodName=(String)in.readObject();
//获取客户端传过来的参数
Object[]args=(Object[])in.readObject();
//获取客服的端传过来的参数类型
Class[]argsType=(Class[])in.readObject();
//从注册表中获取服务的字节码
ClassaClass=RESmap.get(className);
//通过字节码对象获取构造器
Constructorconstructor=aClass.getConstructor();
constructor.setAccessible(true);
//通过反射的方式创建对象并且执行对象的方法
Objectinvoke=aClass.getMethod(methodName,argsType).invoke(constructor.newInstance(),args);
//把返回结果写回给客户端
ObjectOutputStreamreturnObject=newObjectOutputStream(accept.getOutputStream());
returnObject.writeObject(invoke);
//关闭流
in.close();
returnObject.close();
accept.close();
}catch(IOExceptione){
e.printStackTrace();
}catch(ClassNotFoundExceptione){
}catch(IllegalAccessExceptione){
}catch(InstantiationExceptione){
}catch(NoSuchMethodExceptione){
}catch(InvocationTargetExceptione){
}
}).start();
}
这里我们来建立一个服务端
首先定义一个接口
packagerpc.socket.service;
publicinterfaceGODService{
StringgetGod(StringlockMessage,Stringofferings);
实现类
publicclassGODServiceImplimplementsGODService{
publicStringgetGod(StringlockMessage,Stringofferings){
System.out.println(的供品:+offerings);
System.out.println(你的愿望:+lockMessage);
return年轻人还是少做梦!;
写一个服务端的启动类
packagerpc.socket.demo;
importrpc.socket.RpcServer;
publicclassServiceStart{
publicstaticvoidmain(String[]args)throwsIOException{
newRpcServer().Handler();
然后是客服端去调用
importrpc.socket.Clinet;
importrpc.socket.RpcClint;
publicclassclintDemo{
publicstaticvoidmain(String[]args){
ClinetClint=newRpcClint();
GODServiceservice=(GODService)Clint.getService(GODService.class);
StringlockReturn=service.getGod(请给让我中彩票吧,献祭我老板的二十年寿命!);
System.out.println(lockReturn);
我们来看两遍的结果
客服端结果
我来看服务端结果
很显然调用成功了
现在我们来画图模拟一下调用过程