暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

分布式之分布式通信--远程调用

青阳大君 2021-10-08
609

本地调用:单个进程之间的通信通过方法之间的方法名调用,运行时找到内存中方法实际的入口地址,就可完成方法间的调用;

那如果在分布式系统中为了多进程协作共同完成某一任务,且要调用的方法或者函数不再本进程内,那通过何种通信方式能完成这一目的。远程调用此时就可以发挥作用。


远程调用(Remote Procedure Call):进程在调用另一个机器进程的方法时,不关注两个进程底部的通信细节,像调用本地进程方法一般调用另一个机器进程的方法。


说到rpc,不能不聊到http/https。不少数量的开发者会认为rpc和http/https是两种不同形式的调用方式。但实际上rpc包含了序列化协议和传输协议,而http/https只是传输协议。其实rpc完全也可以使用http/https作为传输协议使用。所以说rpc不依赖具体的传输协议,只不过是使用场景上不同而已。此处也可知道远程调用的第一个属性--网络传输协议。


以下是RPC的调用构成

创建stub functions让开发者认为是在调用本地方法;

client stub(proxy):打包参数,调用server方法

server stub (skeleton):接收请求,调用server本地方法


    图片来源:https://people.cs.rutgers.edu/~pxk/417/notes/rpc.html
    复制

    1、客户端调用stub

    2、stub 将参数打包,并序列化发送网络信息

    3、网络信息发送到server

    4、接收信息并发送到server stub

    5、反序列化参数,并调用server 方法

    6、返回执行结果

    7、序列化返回结果并发送网络信息

    8、网络信息发送到client

    9、client stub接受网络信息

    10、client stub反序列化结果,并发送给client


    以上10步,是一个rpc过程的拆解,其中2~8被封装起来,用户看不到调用细节,就像是调用本地方法一样调用远程方法。

    同样,也可发现rpc第二个属性,序列化与反序列化。



    本文的开头也提到了,本地调用可以直接通过方法在内存中的地址来达到调用目的,但是远程调用如此实现不了。解决办法就是需要在client stub在调用时传递‘类名+方法名’,对应server sleketon也能通过维护的service映射表在表中找到对应的方法。此处便是RPC第三个属性,调用类+方法的映射。


    目前Java使用比较多的RPC方案主要有RMI(JDK自带)、Dubbo、Hessian以及Thrift等。


    RMI(Remote Method Invocation)


      图片来源:https://people.cs.rutgers.edu/~pxk/417/notes/pdf/02c-rpc-studies-slides.pdf
      复制

      1、在服务器上运行rmi registry,Server中的Remote object 将对象绑定到注册表registry上;

        Stuff obj = new Stuff();
        Naming.bind("MyStuff", obj);
        复制

        2、client lookup 名称来查找对象,获取远程对象引用以执行远程对象调用;

          MyInterface test = (MyInterface)
          Naming.lookup("rmi://www.pk.org/MyStuff");
          复制

          3、rmi registry service 返回test对象给本地stub; 现在本地stub知道去哪里发送请求并调用远端方法

            test.func(1, 2, "hi");
            复制

            序列化与反序列化,传输层的走向以及stub和skeleton的作用与rpc一致,不再赘述。


            RPC与RMI的区别

            • 1、调用方式:

              • RMI调用方法,RMI中是通过在客户端的Stub对象作为远程接口进行远程方法的调用。每个远程方法都具有方法签名(例如上文例子中的Mystuff)。如果一个方法在服务器上执行,但是没有相匹配的签名被绑定到服务器registry上,那么这个新方法就不能被RMI客户方所调用。

              • RPC调用函数,RPC中是通过网络服务协议向远程主机发送请求,请求包含了一个参数集和一个文本值,通常形成“classname.methodname(参数集)”的形式。这就向RPC服务器表明,被请求的方法在“classname”的类中,名叫“methodname”。然后RPC服务器就去搜索与之相匹配的类和方法,并把它作为那种方法参数类型的输入。这里的参数类型是与RPC请求中的类型是匹配的。一旦匹配成功,这个方法就被调用了,其结果被编码后通过网络协议发回。

            • 2、语言支持:

              • RMI只用于Java,支持传输对象。

              • RPC是基于C语言的,不支持传输对象,是网络服务协议,与操作系统和语言无关。

            • 3、调用结果的返回形式:

                  1、RMI是面向对象的,Java是面向对象的,RMI的调用结果可以是对象类型或者基本数据类型。

                  2、RPC的结果统一由外部数据表示(External Data Representation,XDR)语言表示,这种语言抽象了字节序类和数据类型结构之间的差异。只有由XDR定义的数据类型才能被传递,可以说RMI是面向对象方式的Java RPC。

            • 4、定位  

              • RPC:一种网络服务协议框架;

              • RMI:基于对象的RPC具体实现,应用编程接口API;



            Dubbo

            Dubbo 与 Dubbo协议是两个概念,前者指的是SOA服务治理的框架,而后者指的是Dubbo用来实现rpc的协议。Dubbo支持包括dubbo、rmi、hessian、http、webservice、thrift等网络传输协议,其中dubbo协议是其缺省协议。


            Dubbo 2.0 框架定义了私有的RPC协议,其中请求和响应协议的具体内容用表格来展示,如下。


            具体协议详情见下面链接,不重复造轮子。

            dubbo 2.0 协议
            https://zhuanlan.zhihu.com/p/98562180


            Dubbo 3.0 经过Dubbo 1.0和2.0的演进,推出了Triple议,具体可见下面链接:

            Dubbo3 支持的通信协议
            https://dubbo.apache.org/zh/docs/concepts/rpc-protocol/

            Dubbo3.0相对于之前的协议,升级和完善了许多功能,具体可见上面链接。但是有一条特别强调的是,升级之后的Triple协议支持Streaming的场景,之前版本的协议下dubbo 采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。缺点是对大数据量,比如传文件或者视频等支持不友好,存在调用超时风险。而支持Streaming之后,无疑对这一点有着至关重要的提升,引用上文链接中的一段文字:

            通过Triple协议的Streaming RPC方式,会在consumer跟provider之间建立多条用户态的长连接,Stream。同一个TCP连接之上能同时存在多个Stream,其中每条Stream都有StreamId进行标识,对于一条Stream上的数据包会以顺序方式读写。

            在上面分析完dubbo协议之后,我们在本文RPC模块中描述到,RPC包含三个方面:1、网络传输协议  2、序列化和反序列化 3、映射关系

            网络协议已经说过,序列化和反序列化采用的是Hessian 二进制序列化,会在下面hessian模块中说到。而映射关系,dubbo作为当下比较流行的服务治理框架,集成注册中心register(zookeeper、nacos等)协调client和server地址(ip、port、方法名)的注册与发现。



            Hessian

            Hessian 是 Caucho 开源的一个 RPC 框架,其通讯效率高于 WebService 和 Java 自带的序列化。

            依赖引入:

              <dependency>
              <groupId>com.caucho</groupId>
              <artifactId>quercus</artifactId>
              <version>4.0.65</version>
              </dependency>
              复制

              特性

              • 连接个数:多连接

              • 连接方式:短连接

              • 传输协议:HTTP

              • 传输方式:同步传输

              • 序列化:Hessian二进制序列化

              • 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。

              • 适用场景:页面传输,文件传输,或与原生hessian服务互相操作


              约束
              1、参数及返回值需实现Serializable接口
              2、参数及返回值不能自定义实现List, Map, Number, Date, Calendar等接口,只能用JDK自带的实现,因为hessian会做特殊处理,自定义实现类中的属性值都会丢失。

              协议原理见下:

              hessian协议原理

              https://www.cnblogs.com/shangxiaofei/p/4222170.html



              Thrift

              赖引入:

                <dependency>
                <groupId>org.apache.thrift</groupId>
                <artifactId>libthrift</artifactId>
                <version>0.15.0</version>
                </dependency>
                复制


                Thrift是一种开源的跨语言的RPC服务框架。Thrift最初由facebook公司开发的,在2007年facebook将其提交apache基金会开源了。对于当时的facebook来说创造thrift是为了解决facebook系统中各系统间大数据量的传输通信以及系统之间语言环境不同需要跨平台的特性。所以thrift可以支持多种程序语言。

                在多种不同的语言之间通信thrift可以作为二进制的高性能的通讯中间件,支持数据(对象)序列化和多种类型的RPC服务。Thrift是IDL(interface definition language)描述性语言的一个具体实现,Thrift适用于程序对程序静态的数据交换,需要先确定好它的数据结构,它是完全静态化的,当数据结构发生变化时,必须重新编辑IDL文件,代码生成,再编译载入的流程,跟其他IDL工具相比较可以视为是Thrift的弱项,Thrift适用于搭建大型数据交换及存储的通用工具,对于大型系统中的子系统间数据传输相对于JSON和xml无论在性能、传输大小上有明显的优势。


                详见以下链接:

                Thrift RPC框架介绍

                http://www.blogjava.net/ldwblog/archive/2014/12/03/421011.html


                综上,介绍了RPC的原理以及RMI、Dubbo、Hessian、Thrift四种RPC方式。阅完全文可知协议是RPC的核心,它规范了数据在网络中的传输内容和格式。但是rpc不仅仅是传输协议,还包括传输内容的序列化与反序列化协议、客户端与服务端方法的映射关系。而无论是SUN的RMI、阿里巴巴的Dubbo、Hessian、还是facebook的thirft都是对不同协议的不同实现,都为编程人员提供了应用PRC技术的程序接口(API)。


                开发时如何进行选型:

                • 是否允许代码侵入: 即需要依赖相应的代码生成器生成代码,比如Thrift。

                • 是否需要长连接获取高性能: 如果对于性能需求较高,果断选择基于TCP的Thrift、Dubbo。

                • 是否需要跨越网段、跨越防火墙:这种情况一般选择基于HTTP协议的Hessian和Thrift的HTTP Transport。实际情况是,如果是这种情况,一般不走RPC 而是直接spring-cloud fegin(http/https)调用了。


                此外,Google推出的基于HTTP2.0的gRPC框架也开始得到应用,其序列化协议基于Protobuf,网络框架使用的是Netty4,但是其需要生成代码。此外,dubbo3.0 Triple协议也兼容了gRPC,dubbo3.0 客户端/服务端可以与原生grpc客户端打通。

                有兴趣可参考:

                gRPC 官方文档中文版V1.0

                http://doc.oschina.net/grpc?t=58008
                文章转载自青阳大君,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                评论