分布式架构的基石.简单的 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 ...
随机推荐
- springboot 学习之路 5(打成war包部署tomcat)
目录:[持续更新.....] spring 部分常用注解 spring boot 学习之路1(简单入门) spring boot 学习之路2(注解介绍) spring boot 学习之路3( 集成my ...
- CSS之表格边框合并、兄弟标签外边距合并、父子标签的外边距合并
本文内容: 表格边框合并 兄弟标签外边距合并 父子标签的外边距合并 首发日期:2018-05-01 表格边框合并: 发生情况: 当设置了cellpadding="0" cellsp ...
- VS开发程序用户防范安全问题
一个开发的系统程序从需求.设计到打包.用户使用的过程中,安全问题一直是开发者关注的焦点.对于用户来说,不考虑加密工具(如加密精灵等),面对的是一个系统的各个组件集合及各类的配置文件( 如App.Con ...
- LeetCode题解之Squares of a Sorted Array
1.题目描述 2.问题分析 使用过两个计数器. 3.代码 class Solution { public: vector<int> sortedSquares(vector<int& ...
- Linux中对逻辑卷的移除
移除前先df -mT 看一下:(在上一篇的基础上:Linux中对逻辑卷进行扩容) 1.取消挂载同时删除/etc/fstab下的记录 取消挂载 umount /dev/zhi/lv-zhi 删除记录 v ...
- windows 获取用户的Sid的方法
正常获取: whoami /user 如果要获取其他用户的SID就显得力不从心了,我们可以使用微软提供的系统工具 Sysinternals Suite 下载地址:https://docs.micros ...
- sklearn使用——最小二乘法
参考网页:http://sklearn.apachecn.org/cn/0.19.0/ 其中提供了中文版的文件说明,较为清晰. from sklearn.linear_model import Lin ...
- LeetCode算法题-Plus One(Java实现)
这是悦乐书的第156次更新,第158篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第15题(顺位题号是66).给定一个非空数字数组来表示一个非负整数,并给其加1.该数组已 ...
- node基础—process对象(管理进程)
process对象概述 process对象是一个全局对象,可以在任何地方都能访问到他,通过这个对象提供的属性和方法,使我们可以对当前运行的程序的进程进行访问和控制 process 对象是一个 glob ...
- 11175-From D to E and Back(思维)
Problem UVA11175-From D to E and Back Accept: 164 Submit: 607Time Limit: 3000 mSec Problem Descript ...