分布式架构的基石.简单的 RPC 框架实现(JAVA)
前言
RPC 的全称是 Remote Procedure Call,它是一种进程间通信方式。允许像调用本地服务一样调用远程服务。
学习来源:《分布式系统架构:原理与实践》 - 李林锋
1.RPC 框架原理
RPC 框架的目标就是让远程过程(服务)调用更加简单、透明,RPC框架负责屏蔽底层的传输方式(TCP 或者 UDP)、序列化方式(XML、JSON、二进制)和通信细节。
框架使用者只需要了解谁在什么位置,提供了什么样的远程服务接口即可,开发者不需要关心底层通信细节和调用过程。
2.最简单的 RPC 框架实现
· 下面通过 JAVA 原生的序列化、TCP Socket通信、动态代理和反射机制,实现最简单的 RPC 框架。它由三部分组成:
- 服务提供者:它运行在服务端,负责提供服务接口定义和服务实现类。(EchoService 和 EchoServiceImpl)
- 服务发布者,它运行在 RPC 服务端,负责将本地服务发布成远程服务,供其他消费者调用。(RPCExporter)
- 本地服务代理,它运行在 RPC 客户端,通过代理调用远程服务提供者,然后将结果进行封装返回给本地消费者。(RPCImporter)
下面看具体代码,首先是服务端接口定义和服务实现类。
代码清单 :EchoService
package com.rpc.test; /**
* @Description - 调用接口
* @Author zww
* @Date 2018/12/10 17:29
*/
public interface EchoService {
String echo(String ping);
}
代码清单:EchoServiceImpl
package com.rpc.test; /**
* @Description - 调用接口实现
* @Author zww
* @Date 2018/12/10 17:30
*/
public class EchoServiceImpl implements EchoService {
@Override
public String echo(String ping) {
return ping != null ? ping + "挺不错的。" : "挺不错的。";
}
}
RPC 服务端发布者代码实现如下:
代码清单:RPCExporter
package com.rpc.test; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors; /**
* @Description - 服务端发布者(提供服务)
* @Author zww
* @Date 2018/12/10 17:33
*/
public class RPCExporter {
//线程池
static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); public static void exporter(String hostName, int port) throws Exception {
ServerSocket server = new ServerSocket(); //店家
server.bind(new InetSocketAddress(hostName, port)); //开店地址
try {
while (true) { //开启营业模式
executor.execute(new ExporterTask(server.accept())); //accept : 来客人了
}
} finally {
server.close();
}
} //根据约定规则解析请求,返回结果
private static class ExporterTask implements Runnable {
Socket client = null; //客户
public ExporterTask(Socket client) {
this.client = client;
} @Override
public void run() {
ObjectInputStream inputStream = null;
ObjectOutputStream outputStream = null;
try {
System.out.println("老板娘:诶,来咯!您要点什么!");
inputStream = new ObjectInputStream(client.getInputStream()); //接收请求
System.out.println("老板娘:要个回锅肉!");
String interfaceName = inputStream.readUTF();
Class<?> service = Class.forName(interfaceName);
System.out.println("老板娘:微辣!");
String methodName = inputStream.readUTF();
System.out.println("老板娘:少油!");
Class<?>[] parameterType = (Class<?>[])inputStream.readObject();
System.out.println("老板娘:别放香菜!");
Object[] arguments = (Object[])inputStream.readObject();
System.out.println("老板娘:做菜快点!");
Method method = service.getMethod(methodName, parameterType);
Object result = method.invoke(service.newInstance(), arguments);
System.out.println("老板娘:老头子,听清没!");
System.out.println("老板闷头做菜中!!!!");
System.out.println("老板娘:帅哥,你的菜好了!");
outputStream = new ObjectOutputStream(client.getOutputStream()); //返回结果
outputStream.writeObject(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (client != null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
服务发布者的主要职责:
- 监听客户端的 TCP 连接,接收到新的客户端连接之后,将其封装成 Task,由线程池执行。
- 将客户端发送的码流反序列化成对象,反射调用服务实现者,获取执行结果。
- 将执行结果反序列化,通过 Socket 发送给客户端。
- 远程服务调用完成后,释放 Socket 等连接资源,防止句柄泄露。
RPC 客户端本地服务代理代码:
代码清单:RPCImporter
package com.rpc.test; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket; /**
* @Description - 路人(请求服务)
* @Author zww
* @Date 2018/12/11 10:31
*/
public class RPCImporter<S> { public S importer(final Class<?> serviceClass, final InetSocketAddress address) {
//启用远端代理
return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[]{ serviceClass.getInterfaces()[0] }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = null;
ObjectOutputStream outputStream = null;
ObjectInputStream inputStream = null;
try {
socket = new Socket();
socket.connect(address);
//使用 TCP 方式请求远端方法,以下为约定的传输方式
System.out.println("路人:老板娘,点菜咯!");
outputStream = new ObjectOutputStream(socket.getOutputStream()); //发送请求
System.out.println("路人:要个回锅肉");
outputStream.writeUTF(serviceClass.getName());
System.out.println("路人:微辣!");
outputStream.writeUTF(method.getName());
System.out.println("路人:少油!");
outputStream.writeObject(method.getParameterTypes());
System.out.println("路人:别放香菜!");
outputStream.writeObject(args);
System.out.println("路人:上菜快点!");
inputStream = new ObjectInputStream(socket.getInputStream()); //获取结果
System.out.println("路人:吧唧吧唧!");
return inputStream.readObject();
} finally {
if (socket != null) socket.close();
}
}
});
} }
本地服务代理的主要功能如下:
- 将本地的接口调用转换成 JDK 的动态代理,在动态代理中实现接口的远程调用。
- 创建 Socket 客户端,根据指定地址链接远程服务提供者。
- 将远程服务调用所需的接口类、方法名、参数列表、返回参数 等编码后发送给服务提供者。
- 同步阻塞服务端返回应答,获取应答之后返回。
最后:编写测试代码,并看看执行结果
package com.rpc.test; import java.net.InetSocketAddress; /**
* 测试类
*/
public class TestApplication {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
//启用服务提供端(设置 地址端口)
RPCExporter.exporter("localhost", 8080);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); //发起服务请求
RPCImporter<EchoService> importer = new RPCImporter<>();
//使用远端代理(访问 地址端口)
EchoService echo = importer.importer(EchoServiceImpl.class, new InetSocketAddress("localhost", 8080));
System.out.println(echo.echo("这家店味道咋样? \n"));
}
}
执行测试结果:
Connected to the target VM, address: '127.0.0.1:57656', transport: 'socket'
路人:老板娘,点菜咯!
老板娘:诶,来咯!您要点什么!
路人:要个回锅肉
路人:微辣!
路人:少油!
路人:别放香菜!
路人:上菜快点!
老板娘:要个回锅肉!
老板娘:微辣!
老板娘:少油!
老板娘:别放香菜!
老板娘:做菜快点!
老板娘:老头子,听清没!
老板闷头做菜中!!!!
老板娘:帅哥,你的菜好了!
路人:吧唧吧唧!味道不错
这家店味道咋样?
挺不错的。
分布式架构的基石.简单的 RPC 框架实现(JAVA)的更多相关文章
- Java实现简单的RPC框架(美团面试)
一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...
- 为什么说要搞定微服务架构,先搞定RPC框架?
今天开始聊一些微服务的实践,第一块,RPC框架的原理及实践,为什么说要搞定微服务架构,先搞定RPC框架呢? 一.需求缘起 服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现大公司跨团队 ...
- 【58沈剑架构系列】为什么说要搞定微服务架构,先搞定RPC框架?
第一章聊了[“为什么要进行服务化,服务化究竟解决什么问题”] 第二章聊了[“微服务的服务粒度选型”] 今天开始聊一些微服务的实践,第一块,RPC框架的原理及实践,为什么说要搞定微服务架构,先搞定RPC ...
- 为什么说要搞定微服务架构,先搞定RPC框架
今天开始聊一些微服务的实践,第一块,RPC框架的原理及实践,为什么说要搞定微服务架构,先搞定RPC框架呢? 一.需求缘起 服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现大公司跨团队 ...
- 最简单的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(远程过程调用)简单来说就是调用远程的 ...
- Java实现简单的RPC框架
一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...
- Java 实现简单的RPC框架
0 引言 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).He ...
随机推荐
- 章节三、6-Getters-Setters和this关键字part02
一.如何在一个类中创建另外一个类的对象,然后用这个类的对象的引用来访问这个对象里面的成员,如下: //如何在一个类中创建另外一个类的对象,然后用这个类的对象的引用来访问这个对象里面的成员 Car bm ...
- gitlab runner安装与使用
今天来讲一下如何使用gitlab-runner 下载runner,根据自己对应服务器的型号自行选择下载: # Linux x86- sudo wget -O /usr/local/bin/gitlab ...
- mysql之数据备份与恢复
本文内容: 复制文件法 利用mysqldump 利用select into outfile 其它(列举但不介绍) 首发日期:2018-04-19 有些时候,在备份之前要先做flush tables , ...
- java实现wc
github项目传送门:https://github.com/yanghuipeng/wc 项目要求 wc.exe 是一个常见的工具,它能统计文本文件的字符数.单词数和行数.这个项目要求写一个命令行程 ...
- linux vbundle插件配置
1.新建目录,clone源码 mkdir ~/.vim/bundle/ git clone https://github.com/gmarik/vundle.git ~/.vim/bundle/vun ...
- C#编辑EXE使用的appSettings节点的Config文件
/// <summary> /// 保存配置文件的设定 /// </summary> /// <param name="Key"></pa ...
- [Hive_add_7] Hive 实现最高气温统计
0. 说明 Hive 通过 substr() 函数实现最高气温统计 1. Hive 实现最高气温统计 1.1 思路 将一行文本加载为 String 通过 substr() 函数截取年份和温度 1.2 ...
- Kali 2.0 Web后门工具----WebaCoo、weevely、PHP Meterpreter
注:以下内容仅供学习使用,其他行为均与作者无关!转载请注明出处,谢谢! 本文将介绍 Kali 2.0 版本下的三款Web后门工具:WebaCoo.weevely.PHP Meterpreter,这类工 ...
- LeetCode算法题-Merge Sorted Array(Java实现)
这是悦乐书的第161次更新,第163篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第20题(顺位题号是88).给定两个排序的整数数组nums1和nums2,将nums2中 ...
- 使用freemarker生成静态页面
一 说明 需要在spring mvc项目中加入下列包: <dependency> <groupId>org.freemarker</groupId> <art ...