先来解释解释一下rpc,首先很多人以为rpc是一种协议,其实这个就是出错误的,rpc:是远程过程调用;
看他的全程英文Remote Position Control 他其实是一种设计思想而已,解决分布式各个系统之间的调用关系。
我们今天就用socket方式实现一套rpc调用框架,不多说上代码
package rpc.socket;
//先定义一个clinet接口
public interface Clinet<T> {
T getService(Class<T> tClass);
}
这是我个人写的一个实现类,给位大牛可以尝试实现
这里是采用动态代理。把调用大的过程交给代理对象,这样就可以屏蔽掉底层的网络和整个调用过程,
对于客服而言只用给一个接口的class对象,他会帮你去找到服务端实现类,实现远程调用
package rpc.socket;
import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.util.Properties;
public class RpcClint implements Clinet {
final static String RPC_SERVER_HOST="RPC_SERVER_HOST";
final static String RPC_SERVER_PORT="RPC_SERVER_PORT";
@Override
public Object getService(Class aClass) {
return Proxy.newProxyInstance(aClass.getClassLoader(), new Class[]{aClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//读取配置文件
Properties properties = new Properties();
InputStream resourceAsStream = aClass.getClassLoader().getResourceAsStream("rpc/RpcConfig.properties");
properties.load(resourceAsStream);
String host = properties.getProperty(RPC_SERVER_HOST);
Integer port =new Integer((String) properties.get(RPC_SERVER_PORT));
//RPC注册过程
Socket socket = new Socket(host, port);
OutputStream in= socket.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(in);
//告诉服务端调用的是哪个类
objectOutputStream.writeObject(aClass.getName());
//告诉服务端调用的是哪个方法
objectOutputStream.writeObject(method.getName());
//告诉服务端调用方法传入的参数
objectOutputStream.writeObject(args);
//告诉服务端调用方法的参数类型
objectOutputStream.writeObject(method.getParameterTypes());
//完成序列化,刷新
objectOutputStream.flush();
//接受服务端响应结果
ObjectInputStream returnObj=new ObjectInputStream(socket.getInputStream());
Object o = returnObj.readObject();
//关闭流
objectOutputStream.close();
returnObj.close();
resourceAsStream.close();
socket.close();
return o;
}
});
}
}
这是服务端接口
package rpc.socket;
import java.io.IOException;
public interface Server {
void Handler() throws IOException;
}
这是我实现的服务端rpc,就是采用一个nio接受客服端数据,然后去调用服务端的方法
package rpc.socket;
import rpc.socket.service.GODService;
import rpc.socket.service.GODServiceImpl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class RpcServer implements Server {
//装用实现类的bena
static Map<String,Class> RESmap;
//加载所有需要注册的额服务
static {
//这里可以用Aop 加注解的方式来把需要暴露的服务,注册到服务列表里面去
RESmap=new ConcurrentHashMap<>();
//把服务注册到注册表
RESmap.put(GODService.class.getName(),GODServiceImpl.class);
}
@Override
public void Handler() throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
//采取传统的bio处理
while (true){
//等待客服端链接
Socket accept = serverSocket.accept();
new Thread(()->{
try {
//等待客户端输入
ObjectInputStream in= new ObjectInputStream(accept.getInputStream());
//获取客户端传过来的类名称
String className = (String) in.readObject();
//获取客户端客服端传过来的方法名称
String methodName=(String) in.readObject();
//获取客户端传过来的参数
Object[] args = (Object[]) in.readObject();
//获取客服的端传过来的参数类型
Class[] argsType = (Class[]) in.readObject();
//从注册表中获取服务的字节码
Class aClass = RESmap.get(className);
//通过字节码对象获取构造器
Constructor constructor = aClass.getConstructor();
constructor.setAccessible(true);
//通过反射的方式创建对象并且执行对象的方法
Object invoke = aClass.getMethod(methodName,argsType).invoke(constructor.newInstance(), args);
//把返回结果写回给客户端
ObjectOutputStream returnObject=new ObjectOutputStream(accept.getOutputStream());
returnObject.writeObject(invoke);
//关闭流
in.close();
returnObject.close();
accept.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}).start();
}
}
}
这里我们来建立一个服务端
首先定义一个接口
package rpc.socket.service;
public interface GODService {
String getGod(String lockMessage,String offerings);
}
实现类
package rpc.socket.service;
public class GODServiceImpl implements GODService {
@Override
public String getGod(String lockMessage, String offerings) {
System.out.println("的供品:"+ offerings);
System.out.println("你的愿望:"+lockMessage);
return "年轻人还是少做梦!";
}
}
写一个服务端的启动类
package rpc.socket.demo;
import rpc.socket.RpcServer;
import java.io.IOException;
public class ServiceStart {
public static void main(String[] args) throws IOException {
new RpcServer().Handler();
}
}
然后是客服端去调用
package rpc.socket.demo;
import rpc.socket.Clinet;
import rpc.socket.RpcClint;
import rpc.socket.service.GODService;
public class clintDemo {
public static void main(String[] args) {
Clinet Clint = new RpcClint();
GODService service = (GODService)Clint.getService(GODService.class);
String lockReturn = service.getGod("请给让我中彩票吧", "献祭我老板的二十年寿命!");
System.out.println(lockReturn);
}
}
我们来看两遍的结果
客服端结果
我来看服务端结果
’
很显然调用成功了
现在我们来画图模拟一下调用过程
你学废了吗
参考资料:《2020最新Java基础精讲视频教程和学习路线!》
链接:https://blog.csdn.net/qq_42711376/article/details/110286533
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。