牛逼哄哄的 RPC 框架,底层到底什么原理?
1. RPC框架的概念
RPC(Remote Procedure Call)–远程过程调用,通过网络通信调用不同的服务,共同支撑一个软件系统,微服务实现的基石技术。
使用RPC可以解耦系统,方便维护,同时增加系统处理请求的能力。
上面是一个简单的软件系统结构,我们拆分出来用户系统和订单系统做为服务存在,让不同的站点去调用。
只需要引入各个服务的接口包,在代码中调用RPC服务就跟调用本地方法一样,我刚接触到这种调用方式的时候颇为惊奇,我明明调用的就是java语言方法啊(已java为例,现在RPC框架一般都支持多语言),怎么就调用了远程的服务了呢??
2. RPC框架的原理解析
最近自己写了一个简单的RPC框架KRPC,本文原理分析结合中代码,均为该框架源码,RPC与RMI的区别看这篇文章《Java RMI 和 RPC 的区别》。
2.1 流程纵览
如上图所示,我将一个RPC调用流程概括为上图中5个流程,左边3个为客户端流程,右边两个为服务端流程。
下面就各流程进行解析
2.2 客户端调用
服务调用方在调用服务时,一般进行相关初始化,通过配置文件/配置中心 获取服务端地址用户调用。
// 用户服务接口
public interface UserService {
public User genericUser(Integer id,String name,Long phone);
}
//调用方
//服务初始化
KRPC.init("D:\\krpc\\service\\demo\\conf\\client.xml");
UserService service = ProxyFactory.create(UserService.class, "demo","demoService");
User user = service.genericUser(1, "yasin", 1888888888L);
一开始接触RPC调用方法肯定就有疑惑,它不是一个接口吗,直接调用应该没啥效果啊,我也没有引入实现包。
带着这个疑惑,我们就进入下一个知识点,动态代理。
2.3 动态代理
动态代理这东西意如其名,它代理你帮你做事情,动态代理看这篇文章《详解 Java 中的三种代理模式》。
上面我们不说道直接调用一个接口中的方法,并且没有用该接口的实现类调用,那么方法是怎么生效的呢?
可以看到这个用户服务这个service是由ProxyFactory代理工程创造的,在该ProxyFactory#create()方法中就跟一个代理处理器绑定在一起了。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//构造请求request
Request request = new Request();
....
return RequestHandler.request(serviceName, request,returnClass);
}
这个类实现了InvocationHandler接口(JDK提供的动态代理技术),每次去调用接口方法,最终都交由该handler进行处理。
这个环节一般会获取方法的一些信息,例如方法名,方法参数类型,方法参数值,返回对象类型。
同时这个环节会提供序列化功能,一般的RPC网络传输使用TCP(哪怕使用HTTP)传输,这里也要将这些参数进行封装成我们定义的数据接口进行传输。
2.4 网络传输
我们通过将方法参数进行处理后,就要使用发起网络请求,使用tcp传输的就利用socket通信进行传输,这一块我开源项目中使用的同步堵塞的方案进行请求,也可以使用一些非堵塞方案进行请求,效率会更高一些。
2.5 服务端数据接受
这一块使用netty,可以快速一个高性能、高可靠的一个服务端。
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
byte[] responseBytes = RequestHandler.handler(bytes);
ByteBuf resbuf = ctx.alloc().buffer(responseBytes.length);
resbuf.writeBytes(responseBytes);
ctx.writeAndFlush(resbuf);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
上面代码是我项目中使用的服务端代码。关于netty网上学习的资料很多,这里也只是宏观的讲解RPC原理,就不展开。
2.6 真实调用
服务端获取客户端请求的数据后, 调用请求中的方法,方法参数值,通过反射调用真实的方法,获取其返回值,将其序列化封装,通过netty进行数据返回,客户端在接受数据并解析,这就完成了一次rpc请求调用的全过程。
method = clazz.getMethod(request.getMethodName(),requestParamTypes);
method.setAccessible(true);
result = method.invoke(service, requestParmsValues)
上面代码片段为通过反射调用真实方法
2.7 服务端的动态加载
通过2.2到2.6的说明,一次RPC请求过程大致如此,但是一个RPC框架会有很多细节需要处理。
其实在一次请求调用前,服务端肯定要先启动。
服务端作为一个容器,跟我们熟知的tomcat一样,它可以动态的加载任何项目。所以在服务端启动的时候,必须要进行一个动态加载的过程。
在KRPC中,我使用了URLClassLoader动态加载一个指定路径的jar包,任何业务服务的实现所依赖的jar包都可以放入该路径中。
3. 总结
一个RPC框架大致需要动态代理、序列化、网络请求、网络请求接受(netty实现)、动态加载、反射这些知识点。
现在开源及各公司自己造的RPC框架层出不穷,唯有掌握原理是一劳永逸的。
掌握原理最好的方法莫不是阅读源码,自己动手写是最快的。
原文发布时间为:2018-11-02
本文作者:_Yasin
本文来自云栖社区合作伙伴“Java技术栈”,了解相关信息可以关注“Java技术栈”。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
HTTP 502: Whoops, GitLab is taking too much time to respond.
这次排错过程主要是思路,视野打开后会觉得豁然开朗,原来这其实是个小问题[尴尬]。 1、没注重应用启动的各服务及其用途,只会简单查看 status; 2、看到错误第一时间想到的是 Baidu(没其他意思),找找 logpath 先看日志不好吗? 3、未认识到服务之间的关联关系(比如 postgresql 与 unicorn 之间),前面一直知道 unicorn 启动后没正常监听到端口,但是日志并没啥特别信息(嗯,可能是因为看错了文件)[苦笑] 一、错误信息 二、排错过程 1、启动 unicorn 未监听端口 日志路径 : /var/log/gitlab/unicorn/unicorn_stderr.log PG::ConnectionBad: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/opt/gitlab/postgresql/.s.PGSQL.5432"...
- 下一篇
类的加载机制
目录介绍 01.Java对象的创建过程 1.0 看下创建类加载过程 1.1 对象的创建 1.2 对象的内存布局 02.Java内存区域 2.0 运行时数据区域 2.1 程序计数器 2.2 虚拟机栈 2.3 本地方法栈 2.4 Java堆 2.5 方法区 2.6 运行时常量池 2.7 直接内存 03.Java对象的访问定位方式 3.1 句柄 3.2 直接指针 04.Java对象销毁分析 4.1 JVM内存分配与回收 4.2 判断对象是否死亡 4.3 不可达的对象并非“非死不可” 4.4 如何判断一个常量是废弃常量 4.5 如何判断一个类是无用的类 4.6 GC回收算法详解 05.String类和常量池 5.1 String对象的两种创建方式 5.2 String类型的常量池 好消息 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近2...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装Nodejs环境
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Linux系统CentOS6、CentOS7手动修改IP地址
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16