内容简介:RPC(Remote Procedure Call,远程过程调用)一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地资源一样,通过网络传输去访问远端系统资源。(此段百度的,不管来自哪儿,弄明白就是自己的)下面来展示下本人拙劣的画图技术Client:客户端,服务消费者,负责发起RPC调用,通过远程代理对象调用远程服务。 Serialization/Deserilization:负责对RPC调用通过网络传输的内容进行序列化与反序列化。 Stub Proxy:代理对象,屏蔽RPC调用过程中复
RPC(Remote Procedure Call,远程过程调用)一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地资源一样,通过网络传输去访问远端系统资源。(此段百度的,不管来自哪儿,弄明白就是自己的)
RPC框架实现的架构原理:
下面来展示下本人拙劣的画图技术
Client:客户端,服务消费者,负责发起RPC调用,通过远程代理对象调用远程服务。 Serialization/Deserilization:负责对RPC调用通过网络传输的内容进行序列化与反序列化。 Stub Proxy:代理对象,屏蔽RPC调用过程中复杂的网络处理逻辑,使得RPC调用透明化,能够保持与本地调用一样的代码风格。 TransPort:作为RPC框架底层的通信传输模块,一般通过Socket在客户端与服务端之间传递请求与应答信息。
我所理解的原理过程(不对的地方求指教):
- 服务消费方(client)调用以本地调用方式调用服务;
- stub Proxy接收到调用后负责将方法、参数等组装成可以序列化的Invocation对象;
- stub Proxy找到服务地址,建立socket连接并将消息发送到服务端;
- server端将接收到的数据反序列化,交给stub Proxy处理;
- server端将stub收集到的调用信息分发给具体的方法去执行,这里并不是真正的client去执行该方法,而是在server端通过反射生成的client动态对象去执行;
- RPC接口执行完毕,返回执行结果,思路类似。
RPC的简易实现
技术方案
采用的是Socket通信、动态代理与反射与 Java 原生的序列化,由于能力有限,只会基于BIO来实现,以后会尝试用zookeeper、netty、 redis 等来实现!
具体实现
1、服务提供者提供的服务实现
package com.ooliuyue.simplRpc.test; /** * @Auther: ly * @Date: 2019/3/14 11:45 */ public class HelloServiceImpl implements HelloService { @Override public String hello(String name) { return "hello " + name; } } 复制代码
2、客户端获取远程服务,服务端暴露自己的服务,Socket来完成通信工作(注释比较详细,一目了然)
package com.ooliuyue.simplRpc.rpc; import com.ooliuyue.simplRpc.utils.StringUtils; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.ServerSocket; import java.net.Socket; /** * @Auther: ly * @Date: 2019/3/13 17:53 */ public class SimpleRpc { /** *将指定服务暴露出来,供客户端远程调用 * @param service 需要暴露的服务 * @param port 暴露的端口号 * @throws Exception */ public static void export(final Object service,int port) throws Exception{ if (service == null) throw new IllegalArgumentException("service not null"); if (port < 0 || port > 65535) throw new IllegalArgumentException("port in [0,65535]"); System.out.println("Export Service:" + service.getClass().getName() + " port:" + port ); /** * Socket编程步骤 * 服务器端创建ServerSocket对象,调用accept方法返回Socket对象 * 客户端创建Socket对象,通过端口连接到服务器 * 客户端、服务器端都使用Socket中的getInputStream方法和getOutputStream方法获得输入流和输出流, * 进一步进行数据读写操作 */ /** * socket在服务器端上的使用 * 1.getInputStream方法得到的是一个输入流,服务端的Socket对象上的getInputStream方法得到的输入流其实就是从客户端发送给服务器端的数据流。 * 2.getOutputStream方法得到的是一个输出流,服务端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给客户端的数据。 */ ServerSocket serverSocket = new ServerSocket(port); while (true) { final Socket socket = serverSocket.accept(); new Thread(() -> { ObjectInputStream input = null; ObjectOutputStream output = null; try { try { //将客户端发送给服务端的数据流反序列化成对象 input = new ObjectInputStream(socket.getInputStream()); String methodName = input.readUTF(); Class<?>[] paramTypes = (Class<?>[]) input.readObject(); Object[] arguments = (Object[]) input.readObject(); Method method = service.getClass().getMethod(methodName, paramTypes); //反射调用服务实现,获取执行结果 Object result = method.invoke(service, arguments); //将执行结果反序列化,然后发送给客户端 output = new ObjectOutputStream(socket.getOutputStream()); output.writeObject(result); } catch (IOException e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } finally { if (input != null) try { input.close(); } catch (IOException e) { e.printStackTrace(); } if (output != null) try { output.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } /** * 获取远程服务 * @param interfaceClass 服务接口class * @param host 远程IP地址 * @param port 远程端口号 * @param <T> 指定接口的实例 * @return * @throws Exception */ /** * * socket在客户端上的使用 * 1.getInputStream方法可以得到一个输入流,客户端的Socket对象上的getInputStream方法得到输入流其实就是从服务器端返回的数据。 * 2.getOutputStream方法得到的是一个输出流,客户端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给服务器端的数据。 */ public static <T> T getRemoteService(final Class<T> interfaceClass,final String host,final int port) throws Exception { verifyGetRemoteService(interfaceClass,host,port); return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class<?>[]{interfaceClass},(Object proxy, Method method, Object[] args) ->{ Socket socket = new Socket(host,port); System.out.println("get remote service :" + interfaceClass.getName() + " from " + host + ":" + port); //将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeUTF(method.getName()); output.writeObject(method.getParameterTypes()); output.writeObject(args); //result就是从服务器返回的数据 ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); Object result = input.readObject(); return result; }); } private static <T> void verifyGetRemoteService(final Class<T> interfaceClass, final String host, final int port) { if (interfaceClass == null) throw new IllegalArgumentException("interfaceClass not null"); if (!interfaceClass.isInterface()) throw new IllegalArgumentException("interfaceClass not a interface"); if (!StringUtils.isNotBlank(host)) throw new IllegalArgumentException("host not blank"); if (port < 0 || port > 65535) throw new IllegalArgumentException("port in [0,65535]"); } } 复制代码
3、测试 首先服务端暴露服务,运行main
package com.ooliuyue.simplRpc.test; /** * @Auther: ly * @Date: 2019/3/14 11:43 */ import com.ooliuyue.simplRpc.rpc.SimpleRpc; /** * 服务提供者 */ public class Server { public static void main(String[] args) throws Exception { HelloService helloService = new HelloServiceImpl(); SimpleRpc.export(helloService,1000); } } 复制代码
运行结果: Export Service:com.ooliuyue.simplRpc.test.HelloServiceImpl port:1000
然后客户端进行调用,运行main
package com.ooliuyue.simplRpc.test; import com.ooliuyue.simplRpc.rpc.SimpleRpc; /** * @Auther: ly * @Date: 2019/3/14 11:43 */ public class Client { public static void main(String[] args) throws Exception { HelloService remoteService = SimpleRpc.getRemoteService(HelloService.class, "127.0.0.1", 1000); String str = remoteService.hello("王大锤"); System.out.println(str); } } 复制代码
运行结果 get remote service :com.ooliuyue.simplRpc.test.HelloService from 127.0.0.1:1000 hello 王大锤
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 使用函数式实现观察者模式模式
- 设计模式之发布订阅模式(2) Redis实现发布订阅模式
- 设计模式:创建型模式之单例模式的五种实现
- 实践:使用Spring 原生注解来快速实现 策略模式 + 工厂模式
- 如何实现Builder模式
- 策略模式-Golang实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。