源码模仿之RPC
RPC - 远程过程调用,概念不多赘述,可自行百度。
场景
- 统一api接口
- 生产者(提供远程接口调用方)
- 使用者(主动调用远程接口)
代码实现
API接口(公共依赖包)
DemoEntity (实体类)
/**
* 测试对象
* @author GaoYuan
*/
public class DemoEntity implements Serializable{
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
公共接口(需要预先约定好)
public interface IDemoService {
DemoEntity getDemo(String id);
}
远程调用提供者
DemoService (需要接口的具体实现)
/**
* 接口具体实现
* @author GaoYuan
*/
public class DemoService implements IDemoService {
@Override
public DemoEntity getDemo(String id) {
DemoEntity demoEntity = new DemoEntity();
demoEntity.setId("1");
demoEntity.setName("gaoyuan");
return demoEntity;
}
}
ProviderStarter (提供者启动类)
/**
* 接口提供者
* @author GaoYuan
*/
public class ProviderStarter {
public static void main(String[] args){
try {
// 搭建 8080 端口的socket监听服务
ServerSocket serverSocket = new ServerSocket(8989);
while (true){
Socket socket = serverSocket.accept();
// 获取socket的输入流(即其他服务进行请求的参数,主要是获取调用的类名、方法名、参数等)
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
String className = objectInputStream.readUTF();
System.out.println(">>>>>> [接收]:" + className);
String methodName = objectInputStream.readUTF();
System.out.println(">>>>>> [接收]:" + methodName);
Class[] methodParameterTypes = (Class[]) objectInputStream.readObject();
System.out.println(">>>>>> [接收]:" + methodParameterTypes);
Object[] methodArgs = (Object[]) objectInputStream.readObject();
System.out.println(">>>>>> [接收]:" + methodArgs);
Class myclass = null;
// 这里主要是解决如何通过接收到的类名为"IDemoService" 转换为 具体的实现"DemoService"
if(IDemoService.class.getName().equals(className)){
// 此时 myclass已经是具体的实现类了
myclass = ClassLoader.getSystemClassLoader().loadClass("com.foruo.simple.rpc.provider.DemoService");
// myclass = DemoService.class; // 也可以用这种方式获取class
System.out.println(">>>>>> [组装]:指定实现类DemoService");
}
// 获取具体实现类的方法
Method method = myclass.getMethod(methodName, methodParameterTypes);
System.out.println(">>>>>> [组装]:指定方法");
// 获取实现类方法执行的结果
Object invoke = method.invoke(myclass.newInstance(), methodArgs);
System.out.println(">>>>>> [执行]:指定实现类的指定方法");
System.out.println(">>>>>> [执行]:获取方法执行结果");
System.out.println(">>>>>> [回执]:将执行结果输出");
// 将执行结果输出
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(invoke);
objectOutputStream.flush();
// 关闭流
objectInputStream.close();
objectOutputStream.close();
// 关闭socket
socket.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
消费者(使用方)
ConsumerStarter
/**
* 消费者 - 使用远程接口
* @author GaoYuan
*/
public class ConsumerStarter {
public static void main(String[] args){
// 这里实际上相当于获取了IDemoService的实现
IDemoService demoService = (IDemoService)simpleRpc(IDemoService.class);
// 执行getDemo方法时,相当于调用了代理的处理方法
DemoEntity demoEntity = demoService.getDemo("1");
System.out.println(demoEntity.getName());
}
/**
* 远程调用具体代码
* @author GaoYuan
*/
public static Object simpleRpc(Class myClass){
return Proxy.newProxyInstance(myClass.getClassLoader(), new Class[]{myClass}, (proxy, method, args) -> {
// socket连接8080端口
Socket socket = new Socket("127.0.0.1", 8989);
// 获取类名 (这里是获取 IDemoService 接口名)
String className = myClass.getName();
// 获取方法名
String methodName = method.getName();
// 获取方法参数
Class[] methodParameterTypes = method.getParameterTypes();
/**
* 这里主要将 需要调用的目标方法(包含对象类名/接口名)以socket形式传递过去,以调用远程接口
* 就是告诉远程接口,我想要调用什么方法
* */
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeUTF(className);
System.out.println(">>>>>> [发送]:" + className);
objectOutputStream.writeUTF(methodName);
System.out.println(">>>>>> [发送]:" + methodName);
objectOutputStream.writeObject(methodParameterTypes);
System.out.println(">>>>>> [发送]:" + methodParameterTypes);
objectOutputStream.writeObject(args);
System.out.println(">>>>>> [发送]:" + args);
objectOutputStream.flush();
/** 获取远程接口执行结果 */
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
Object o = objectInputStream.readObject();
System.out.println(">>>>>> [接收]:" + o);
// 关闭流
objectInputStream.close();
objectOutputStream.close();
// 关闭socket
socket.close();
return o;
});
}
}
执行
先启动 ProviderStarter ,再启动 ConsumerStarter
输出
ProviderStarter
>>>>>> [接收]:com.foruo.simple.rpc.service.IDemoService
>>>>>> [接收]:getDemo
>>>>>> [接收]:[Ljava.lang.Class;@3c5a99da
>>>>>> [接收]:[Ljava.lang.Object;@47f37ef1
>>>>>> [组装]:指定实现类DemoService
>>>>>> [组装]:指定方法
>>>>>> [执行]:指定实现类的指定方法
>>>>>> [执行]:获取方法执行结果
>>>>>> [回执]:将执行结果输出
ConsumerStarter
>>>>>> [发送]:com.foruo.simple.rpc.service.IDemoService
>>>>>> [发送]:getDemo
>>>>>> [发送]:[Ljava.lang.Class;@6a5fc7f7
>>>>>> [发送]:[Ljava.lang.Object;@6fadae5d
>>>>>> [接收]:com.foruo.simple.rpc.entity.DemoEntity@7a5d012c
gaoyuan
可见,最终消费者获取到了远程调用的返回值。
博客
开源中国博客地址
https://my.oschina.net/gmarshal
个人博客地址
欢迎关注我的个人微信订阅号:(据说这个头像程序猿专用)