Java 实现简单的 RPC 框架
RPC 简介
RPC,全称为 Remote Procedure Call,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式,而不需要了解底层网络技术的协议。 RPC 协议假定某些传输协议的存在,如 TCP 或 UDP,为通信程序之间携带信息数据。如 RMI(远程方法调用)、Hessian、Http invoker 等。
怎样实现一个 RPC 框架
RPC 能够让本地应用简单、高效地调用服务器中的过程。它主要应用在分布式系统。如 Hadoop 中的 IPC 组件。但怎样实现一个 RPC 框架呢?
可以从下面几个方面思考:
- 通信模型:假设通信的为 A 机器与 B 机器,A 与 B 之间有通信模型,在 Java 中一般基于 BIO 或 NIO。
- 过程(服务)定位:使用给定的通信方式,与确定 IP 与端口及方法名称确定具体的过程或方法;
- 远程代理对象:本地调用的方法(服务)其实是远程方法的本地代理,因此可能需要一个远程代理对象,对于 Java 而言,远程代理对象可以使用 Java 的动态对象实现,封装了调用远程方法调用;
- 序列化,将对象名称、方法名称、参数等对象信息进行网络传输需要转换成二进制传输,这里可能需要不同的序列化技术方案。如:protobuf,Arvo 等。
RPC 框架架构
RPC 架构分为三部分:
- 服务提供者,运行在服务器端,提供服务接口定义与服务实现类。
- 服务中心,运行在服务器端,负责将本地服务发布成远程服务,管理远程服务,提供给服务消费者使用。
- 服务消费者,运行在客户端,通过远程代理对象调用远程服务。
RPC 框架的简单实现
这里我只介绍服务提供者和客户端的实现方式。
服务提供者
服务提供者 IHello 接口定义:
public interface IHello {
String sayHello(String string);
}
服务提供者 IHello 接口实现:
public class HelloImpl implements IHello {
@Override
public String sayHello(String string) {
return "Hello:" + string;
}
}
服务端 RpcProxyServer 类:
public class RpcProxyServer { ExecutorService executorService = Executors.newCachedThreadPool(); public void publisher(Object service, int port) {
ServerSocket serverSocket = null;
try {
// 启动 socket 服务
serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
executorService.execute(new ProcessorHandler(service, socket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端 RpcRequest 类:
public class RpcRequest implements Serializable {
private static final long serialVersionUID = 383378368319625542L;
private String className;
private String methodName;
private Object[] params; public String getClassName() {
return className;
} public void setClassName(String className) {
this.className = className;
} public String getMethodName() {
return methodName;
} public void setMethodName(String methodName) {
this.methodName = methodName;
} public Object[] getParams() {
return params;
} public void setParams(Object[] params) {
this.params = params;
} @Override
public String toString() {
return "RpcRequest{" +
"className='" + className + '\'' +
", methodName='" + methodName + '\'' +
", params=" + Arrays.toString(params) +
'}';
}
}
服务端 ProcessorHandler 类:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket; public class ProcessorHandler implements Runnable {
Socket socket;
Object service; public ProcessorHandler(Object service, Socket socket) {
this.socket = socket;
this.service = service;
} @Override
public void run() {
System.out.println("begin processor handler!");
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(socket.getInputStream());
RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();
Object restlt = invoke(rpcRequest); ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(restlt);
objectOutputStream.flush(); objectInputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} private Object invoke(RpcRequest request) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object[] args = request.getParams();
Class<?>[] types = new Class[args.length];
for (int i = 0; i < args.length; i++) {
types[i] = args[i].getClass();
}
Method method = service.getClass().getMethod(request.getMethodName(), types);
return method.invoke(service, args);
}
}
服务端主类 RpcServerMain:
public class RpcServerMain {
public static void main(String[] args) {
IHello hello = new HelloImpl();
RpcProxyServer rpcProxyServer = new RpcProxyServer();
rpcProxyServer.publisher(hello, 8080);
System.out.println(hello.sayHello("charles"));
}
}
客户端
客户端 IHello 类:
public interface IHello {
String sayHello(String string);
}
客户端 RpcClientProxy 类:
public class RpcClientProxy {
public <T> T clientProxy(Class<T> interfaceCls, String host, int port) {
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{interfaceCls}, new RemoteInvocationHandler(host, port));
}
}
客户端 RpcRequest 类:
public class RpcRequest implements Serializable {
private static final long serialVersionUID = 383378368319625542L;
private String className;
private String methodName;
private Object[] params; public String getClassName() {
return className;
} public void setClassName(String className) {
this.className = className;
} public String getMethodName() {
return methodName;
} public void setMethodName(String methodName) {
this.methodName = methodName;
} public Object[] getParams() {
return params;
} public void setParams(Object[] params) {
this.params = params;
} @Override
public String toString() {
return "RpcRequest{" +
"className='" + className + '\'' +
", methodName='" + methodName + '\'' +
", params=" + Arrays.toString(params) +
'}';
}
}
客户端 RpcNetTransport 类:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.net.Socket; public class RpcNetTransport { String host;
int port; public RpcNetTransport(String host, int port) {
this.host = host;
this.port = port;
} private Socket createSocket() {
System.out.println("Begin create socket connect!");
Socket socket = null; try {
socket = new Socket(host, port);
} catch (Exception e) {
throw new RuntimeException("build connect failed.");
}
return socket;
} public Object send(RpcRequest rpcRequest) {
Socket socket = null; try {
socket = createSocket();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(rpcRequest);
objectOutputStream.flush(); // 返回结果接收 ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
Object resultObject = objectInputStream.readObject();// 反序列化 对象
objectInputStream.close();
objectOutputStream.close(); return resultObject;
} catch (Exception e) {
throw new RuntimeException("send request exception:" + e);
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端 RemoteInvocationHandler 类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class RemoteInvocationHandler implements InvocationHandler {
String host;
int port; public RemoteInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setClassName(method.getDeclaringClass().getName());
rpcRequest.setMethodName(method.getName());
rpcRequest.setParams(args); RpcNetTransport rpcNetTransport = new RpcNetTransport(host, port);
return rpcNetTransport.send(rpcRequest);
}
}
客户端主类 RpcClientMain:
public class RpcClientMain {
public static void main(String[] args) {
RpcClientProxy rpcClientProxy = new RpcClientProxy();
IHello hello = rpcClientProxy.clientProxy(IHello.class, "localhost", 8080);
System.out.println(hello.sayHello("charles"));
}
}
项目启动后客户端向服务端发送了一条消息,分别运行两个项目后输出结果如下
服务端:
begin processor handler!
客户端:
Begin create socket connect!
Hello:charles
总结
RPC 本质为消息处理模型,RPC 屏蔽了底层不同主机间的通信细节,让进程调用远程的服务就像是本地的服务一样。
Java 实现简单的 RPC 框架的更多相关文章
- Java实现简单的RPC框架(美团面试)
一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...
- Java实现简单的RPC框架
一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...
- Java 实现简单的RPC框架
0 引言 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).He ...
- 分布式架构的基石.简单的 RPC 框架实现(JAVA)
前言 RPC 的全称是 Remote Procedure Call,它是一种进程间通信方式.允许像调用本地服务一样调用远程服务. 学习来源:<分布式系统架构:原理与实践> - 李林锋 1. ...
- 最简单的RPC框架实现
通过java原生的序列化,Socket通信,动态代理和反射机制,实现一个简单的RPC框架,由三部分组成: 1.服务提供者,运行再服务端,负责提供服务接口定义和服务实现类 2.服务发布者,运行再RPC服 ...
- 学习写简单的RPC框架demo
学习实现一个简单的RPC框架. 工程主要目录分级结构: rpc-common: 公共基础包,能力提供包 rpc-provider: 服务提供者 rpc-consumer:服务消费者 rpc-servi ...
- 徒手撸一个简单的RPC框架
来源:https://juejin.im/post/5c4481a4f265da613438aec3 之前在牛逼哄哄的 RPC 框架,底层到底什么原理得知了RPC(远程过程调用)简单来说就是调用远程的 ...
- 一个简单的"RPC框架"代码分析
0,服务接口定义---Echo.java /* * 定义了服务器提供的服务类型 */ public interface Echo { public String echo(String string) ...
- 动手实现一个简单的 rpc 框架到入门 grpc (上)
rpc 全称 Remote Procedure Call 远程过程调用,即调用远程方法.我们调用当前进程中的方法时很简单,但是想要调用不同进程,甚至不同主机.不同语言中的方法时就需要借助 rpc 来实 ...
随机推荐
- pecl和pear 的区别和联系
Pear:是PHP的扩展代码包,所有的扩展均以PHP代码的形式出现,功能强大,安装简单,甚至可以改改就用.使用的时候,要在代码中进行Include才能够使用. Pecl:是PHP的标准扩展,可以补充实 ...
- 使用Flask构建机器学习模型API
1. Python环境设置和Flask基础 使用"Anaconda"创建一个虚拟环境.如果你需要在Python中创建你的工作流程,并将依赖项分离出来,或者共享环境设置," ...
- MacOS系统降级
从MacOS 10.14 降级到 10.12,下载好系统镜像文件.打开,复制到Application. 准备一个至少8G的U盘,,打开磁盘工具,『抹掉』(格式化)成Mac OS扩展(日志式),名称可随 ...
- C# 读取文件内容
读取文件内容有三种方式 全部读取到字符串变量中 一次读取一行 全部读取到字符串数组中,每个数组元素存储一行文本 全部读取到字符串变量 string text = System.IO.File.Read ...
- iOS 判断scrollView是否滑动到底部
判断scrollView有没有滚动到视图的底部,用来判断下拉刷新的时间.等 - (void)scrollViewDidScroll:(UIScrollView *)scrollView1 { CG ...
- java大文件下载+断点续传
java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路:1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操 ...
- vue路由传参的三种方式
方式一 通过query方式传参 这种情况下 query传递的参数会显示在url后面 this.$router.push({ path: '/detail', query: { id: id } }) ...
- talend工具通过关键字来取得text文本中的数据
问题描述: 在文件夹中有多个text文本,通过关键字来取得text文本里面我所需要的数据,并出力到Excel里面.取得的数据,在text文本里面可能是多行的存在,并且在Excel里面是一行的存在. 解 ...
- Java面试 - PATH与CLASSPATH 的区别?
PATH:操作系统提供的路径配置,用于定义所有可执行程序的路径. CLASSPATH:由JRE提供的,用于定义Java 程序解释时类加载路径.
- Nginx 413 Request Entity Too Large
用户上传图片的时候,报错. 发现,原来是图片太大导致. 咦?后台配置图片支持5M啊? 哦!原来是Nginx配置问题. Nginx默认支持1M的POST数据! 修改Nginx配置! 修改nginx.co ...