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 框架的更多相关文章

  1. Java实现简单的RPC框架(美团面试)

    一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...

  2. Java实现简单的RPC框架

    一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...

  3. Java 实现简单的RPC框架

    0 引言 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).He ...

  4. 分布式架构的基石.简单的 RPC 框架实现(JAVA)

    前言 RPC 的全称是 Remote Procedure Call,它是一种进程间通信方式.允许像调用本地服务一样调用远程服务. 学习来源:<分布式系统架构:原理与实践> - 李林锋 1. ...

  5. 最简单的RPC框架实现

    通过java原生的序列化,Socket通信,动态代理和反射机制,实现一个简单的RPC框架,由三部分组成: 1.服务提供者,运行再服务端,负责提供服务接口定义和服务实现类 2.服务发布者,运行再RPC服 ...

  6. 学习写简单的RPC框架demo

    学习实现一个简单的RPC框架. 工程主要目录分级结构: rpc-common: 公共基础包,能力提供包 rpc-provider: 服务提供者 rpc-consumer:服务消费者 rpc-servi ...

  7. 徒手撸一个简单的RPC框架

    来源:https://juejin.im/post/5c4481a4f265da613438aec3 之前在牛逼哄哄的 RPC 框架,底层到底什么原理得知了RPC(远程过程调用)简单来说就是调用远程的 ...

  8. 一个简单的"RPC框架"代码分析

    0,服务接口定义---Echo.java /* * 定义了服务器提供的服务类型 */ public interface Echo { public String echo(String string) ...

  9. 动手实现一个简单的 rpc 框架到入门 grpc (上)

    rpc 全称 Remote Procedure Call 远程过程调用,即调用远程方法.我们调用当前进程中的方法时很简单,但是想要调用不同进程,甚至不同主机.不同语言中的方法时就需要借助 rpc 来实 ...

随机推荐

  1. windows系统下Jenkins 持续集成安装使用

    先要下载安装Tomcat,基于Java的web项目可以通过Tomcat运行.下载Jenkins,注意要安装在Tomcat的webapps目录下.安装完Jenkins会自动通过浏览器打开http://l ...

  2. final和finally和finalize的区别

    final 修饰类,不能被继承 修饰方法,不能被重写 修饰变量,只能赋值一次 finally 是try语句中的一个语句体,不能单独使用,用来释放资源 finalize 是一个方法,当垃圾回收器确定不存 ...

  3. 原生app是什么意思?

    原生的就是用 Android 和ios 写的 完全符合手机系统 其他的都是通过各种工具对代码转换为手机系统可以识别

  4. 90后外挂开发者:已经有许多主播在我这里在外挂,我月入50W

    绝地求生上线不到一年已经火爆全球,玩家们表示再差的优化也抵挡不住我们玩游戏的热情,近日,各大平台主播糯米油条五五开等人的开挂实锤闹得沸沸扬扬,玩家之间刮起了一阵反击外挂的风暴. 俗话说得好,没有买卖就 ...

  5. 阶段5 3.微服务项目【学成在线】_day09 课程预览 Eureka Feign_06-Feign远程调用-Ribbon测试

    2.1.2 Ribbon测试 Spring Cloud引入Ribbon配合 restTemplate 实现客户端负载均衡.Java中远程调用的技术有很多,如: webservice.socket.rm ...

  6. Qt编写Onvif搜索及云台控制工具

    一.前言 这个工具很早以前大概在2013年就想做了,后面杂七杂八的事情一再耽搁,记得当时最初用的是soap类来搜索和解析的,后面发现太大了,每次编译都要等好久,光源码文件加起来都快10MB了,而且函数 ...

  7. PAT 甲级 1047 Student List for Course (25 分)(cout超时,string scanf printf注意点,字符串哈希反哈希)

    1047 Student List for Course (25 分)   Zhejiang University has 40,000 students and provides 2,500 cou ...

  8. 第二十一章 授予身份及切换身份——《跟我学Shiro》

    目录贴:跟我学Shiro目录贴 在一些场景中,比如某个领导因为一些原因不能进行登录网站进行一些操作,他想把他网站上的工作委托给他的秘书,但是他不想把帐号/密码告诉他秘书,只是想把工作委托给他:此时和我 ...

  9. 01.轮播图之二 :tableView 轮播

    在做这个tablevew轮播的时候,重要的就是修改frame 和view 的翻转了:::: 也是不难的,概要的设计和scroll 轮播是一致的: 首先是 .h 的文件 @interface Table ...

  10. 在Electron运行的页面使用CSS的calc导致应用卡死

    这几天发现运行在我们开发的Electron里面的网页有部分应用点击访问就会卡死,通过排除法定位到使用了CSS的calc方法,如下: <el-table height="calc(100 ...