介绍
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:域名申请、雅安服务器托管、营销软件、网站建设、于田网站维护、网站推广。
当开发一个单体项目的时候,大家肯定都写过类似的代码。即服务提供方和服务调用方在一个服务中
- public interface HelloService {
- public String sayHello(String content);
- }
- public class HelloServiceImpl implements HelloService {
- @Override
- public String sayHello(String content) {
- return "hello, " + content;
- }
- }
- public class Test {
- public static void main(String[] args) {
- HelloService helloService = new HelloServiceImpl();
- String msg = helloService.sayHello("world");
- // hello world
- System.out.println(msg);
- }
- }
但是由于单体服务的诸多弊端,现在很多公司已经将不相关的功能拆分到不同的服务中。
如何像调用本地服务一样调用远程服务呢?这时就不得不提RPC框架了(Remote Procedure Call,远程过程调用)。他帮我们屏蔽了网络通信,序列化等操作的实现,真正做到了调用远程服务和调用本地服务一样方便。
知名的RPC框架有Spring Cloud,阿里巴巴的Dubbo,Facebook的Thrift,Google grpc等
RPC的调用过程
一个RPC调用的过程如下
下面我们就分析一下rpc框架是怎么实现的?有哪些地方可以扩展。为了让大家有一个更形象的认识,我写了一个github项目,由简到难实现了一个rpc框架,欢迎star
https://github.com/erlieStar/simple-rpc
生成代理类
前面我们说过,调用方执行方法后,实际上执行的是代理类的方法,代理类帮我们进行序列化和编解码操作。那么如何生成代理类呢?
我们看一下主流的做法。
Facebook的Thrift和Google的grpc都是定义一个schema文件,然后执行程序,帮你生成客户端代理类,以及接口。调用方直接用生成的代理类来请求,提供方继承生成的接口即可。
这种方式最大的优点就是能进行多语言通信,即一份schema文件可以生成Java程序,也可以生成Python程序。调用方是Java程序,提供方是Python程序都能正常进行通讯。而且是二进制协议,通讯效率比较高。
在Java中生成代理类的方式有如下几种
在Dubbo中提供了2种生成代理类的方式,jdk动态代理和Javassist,默认是javassist,至于原因吗?当然是javassist的效率更高
协议
为什么需要协议这个东西呢?Spring Cloud是通过Http协议来进行通讯的,那么Dubbo是通过哪种协议来进行通讯的?
为什么需要协议这个东西?
因为数据是以二进制的形式在网络中传输中,RPC的请求数据并不是以一个整体发送到提供方的,而是可能被拆分成多个数据包发送出去,那提供方怎么识别数据呢?
例如一个文本ABCDEF,提供方有可能依次收到的数据为ABC DEF,也有可能为AB CD EF。提供方该怎么处理这些数据呢?
简单啊,定个规则就可以了。这个规则可以有很多种,这里举3个例子
Dubbo通过自定义协议来进行通讯,协议头格式如下
每个位代表的含义如下
Dubbo为什么要自定义协议,而不用现成的Http协议?
最主要的原因就是自定义协议可以提高性能
Http协议的请求包比较大,有很多无用的内容。自定义协议可以精简很多内容
Http协议是无状态的,每次都要重新建立连接,响应完毕后将连接关闭
序列化
协议头的内容是通过位来表示的,协议体在应用程序中则会被封装成对象,如Dubbo将请求封装成Request,将响应封装成Response
前面我们说过网络传输的数据必须是二进制数据,但调用方的入参和提供方的返回值都是对象,因此需要序列化和反序列化的过程
序列化的方式有如下几种
我们选择序列化的方式时,主要考虑如下几个因素
通讯
常见的IO模型有如下四种
因为RPC一般用在高并发的场景下,因此我们选择IO多路复用这种模型,Netty的IO多路复用基于Reactor开发模式来实现,后续的文章我会分析一下这种开发模式是如何支持高并发的
注册中心
注册中心的作用和电话簿类似。保存了服务名称和具体的服务地址之间的映射关系,当我们想和某个服务进行通信时,只需要根据服务名就能查到服务的地址。
更重要的是这个电话簿是动态的,当某个服务的地址改变时,电话簿上的地址就会改变,当某个服务不可用时,电话簿上的地址就会消失
这个动态的电话簿就是注册中心。
注册中心的实现方式有很多种,Zookeeper,Redis,Nocas等都可以实现
介绍一下用Zookeeper实现注册中心的方式
zookeeper有两种类型的节点,持久节点和临时节点
当我们往zookeeper上注册服务的时候,用的是临时节点,这样当服务断开时,节点能被删除
节点类型 | 解释 |
---|---|
持久节点 | 将节点创建为持久节点,数据会一直存储在zookeeper服务器上,即使创建该节点的客户端与服务端的会话关闭了,该节点依然不会被删除 |
持久顺序节点 | 在持久节点的基础上增加了节点有序的特性 |
临时节点 | 将节点创建为临时节点,数据不会一直存储在zookeeper服务器上,当创建该临时节点的客户端会话关闭时,该节点在相应的zookeeper服务器上被删除 |
临时顺序节点 | 在临时节点的基础上增加了节点有序的特性 |
注册中心全部挂掉该怎么通信?
当一台zookeeper挂掉后,会自动切换到另一个zookeeper。全部挂掉也没有关系,因为dubbo把映射关系保存了一份在本地,这个映射关系可以保存在Map中,也可以保存在文件中
新的服务注册到注册中心,本地缓存会更新吗?
注册了监听的话,当然会更新啊。当被监听的节点或者子节点发生变化的时候,会将相应的内容推送给监听的客户端,你就可以更新本地的缓存了
Zookeeper中的事件如下
你可以把这个监听理解为分布式的观察者模式
小结
当然一个成熟的RPC框架还得考虑很多内容,例如路由策略,异常重试,监控,异步调用等,和主流程相关度不大,就不多做介绍了
本文转载自微信公众号「Java识堂」,可以通过以下二维码关注。转载本文请联系Java识堂公众号。
网页标题:如何手写了一个RPC框架
分享地址:http://www.gawzjz.com/qtweb/news3/173603.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联