• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

RPC基本概念、原理和用途

RPC winrains 来源:李金葵 1年前 (2019-09-10) 38次浏览

1、引言

RPC对于分布式系统来说,是一项非常有价值的技术。
就拿IM系统来说,单实例情况下自然没RPC什么事,最多用用MQ消息中间件就能很好的发挥系统效力。但一旦IM用户规模越来越大,单实例络究有出现拼颈的那一天,那么自然而然就要走到IM集群这一步了。
说到IM集群,好像看起来很简单,很多Web后端出身的程序员可能脱口而出——用Nginx解决不就行了?显然不是那么回事,如果你也有这种想法,那么请读一读《即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?》后,再来讨论。
当然,真正要做到可投入到生产级别的IM集群系统,难度还是相当大的,必竟IM这种长连接应用相比传统Http这种短连接应用太不标准,因而现成可用的技术太少,多数时候只能自已造轮子。以一个典型的IM聊天消息传输为例,假设存在两个正在聊天的用户(用户A和用户B),当A连接的是IM集群中的IM实例1、B连接的是IM集群中的IM实例2,此时当用户A向用户B发送一条聊天消息时,这条消息应该如何传递呢?
我们梳理一下这个过程:

  • 1)消息首先会由用户A发往IM实例1;
  • 2)IM实例1会将此条消息转交给IM实例2;
  • 3)IM实例2会将此条消息最终投递给连接在本实例上的用户B。

以上,就是一个IM集群中,一条聊天消息的典型投递流程。
那么,这其中涉及到一个关键步骤:即第2)步中如何实现“IM实例1会将此条消息转交给IM实例2”?
要解决这个问题,就需要用到RPC技术了。本文将带你从基本概念、原理和用途方面,快速理解快速理解RPC技术,以便您在进行IM集群开发时能更好的进行方案设计和实现。

2、相关文章

3、基本概念

RPC(Remote Procedure Call):远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。
RPC 是一种技术思想而非一种具体的规范或协议。
常见 RPC 技术和框架有:

  • 1)应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud。
  • 2)远程通信协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。
  • 3)通信框架:MINA 和 Netty。

目前流行的开源 RPC 框架还是比较多的,有阿里巴巴的 Dubbo、Facebook 的 Thrift、Google 的 gRPC、Twitter 的 Finagle 等。
下面重点介绍当前最流行的三种:

  • 1)gRPC是 Google 公布的开源软件,基于最新的 HTTP 2.0 协议,并支持常见的众多编程语言。RPC 框架是基于 HTTP 协议实现的,底层使用到了 Netty 框架的支持;
  • 2)Thrift是 Facebook 的开源 RPC 框架,主要是一个跨语言的服务开发框架。用户只要在其之上进行二次开发就行,应用对于底层的 RPC 通讯等都是透明的。不过这个对于用户来说需要学习特定领域语言这个特性,还是有一定成本的;
  • 3)Dubbo是阿里集团开源的一个极为出名的 RPC 框架,在很多互联网公司和企业应用中广泛使用。协议和序列化框架都可以插拔是极其鲜明的特色。

4、典型的 RPC 框架

在一个典型 RPC 的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进行网络传输和序列化。
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_1.jpg
▲ 一个典型的 RPC 架构原理图
如下是 Dubbo 的设计架构图,分层清晰,功能复杂:
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_2.jpg
▲ Dubbo 架构图

5、RPC的核心功能

RPC 的核心功能是指实现一个 RPC 最重要的功能模块,就是上图中的”RPC 协议”部分:
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_3.jpg
▲ RPC 核心功能
一个 RPC 的核心功能主要有 5 个部分组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等。
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_4.jpg
▲ RPC 核心功能原理图
下面分别介绍核心 RPC 框架的重要组成:

  • 1)客户端(Client):服务调用方;
  • 2)客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端;
  • 3)服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理;
  • 4)服务端(Server):服务的真正提供者;
  • 5)Network Service:底层传输,可以是 TCP 或 HTTP。

我们用一个demo实例来讲解这个调用过程。
Demo实例中,客户端和服务端的ip地址如下:

客户端 IP:172.171.4.176
服务端 IP:172.171.5.95

通信使用 HTTP 协议,XML 文件传输格式。传输的字段包括:方法名 methodName,两个参数 2,3:
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_5.jpg
▲ Request 抓包
服务端返回结果,字段返回值 Value,结果是 5:
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_6.jpg
▲ Response 抓包
在这两次网络传输中使用了 HTTP 协议,建立 HTTP 协议之间有 TCP 三次握手,断开 HTTP 协议时有 TCP 四次挥手:
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_7.png
▲ 基于 HTTP 协议的 RPC 连接过程
Demo实例详细调用过程,我们来一起看看。
Demo 实现的实现过程,流程和分工角色可以用下图来表示:
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_8.jpg
▲ RPC 调用详细流程图
针对Demo实例,我们可以知道一次典型的 RPC 调用流程如下:

  • 1)服务消费者(Client 客户端)通过本地调用的方式调用服务;
  • 2)客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体;
  • 3)客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端;
  • 4)服务端存根(Server Stub)收到消息后进行解码(反序列化操作);
  • 5)服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理;
  • 6)服务端(Server)本地服务业务处理;
  • 7)处理结果返回给服务端存根(Server Stub);
  • 8)服务端存根(Server Stub)序列化结果;
  • 9)服务端存根(Server Stub)将结果通过网络发送至消费方;
  • 10)客户端存根(Client Stub)接收到消息,并进行解码(反序列化);
  • 11)服务消费方得到最终结果。

5、RPC 核心功能的实现介绍

5.1 概述

RPC 的核心功能主要由 5 个模块组成,如果想要自己实现一个 RPC,最简单的方式要实现三个技术点。
它们分别是:

  • 1)服务寻址;
  • 2)数据流的序列化和反序列化;
  • 3)网络传输。

5.2 服务寻址

服务寻址可以使用 Call ID 映射。在本地调用中,函数体是直接通过函数指针来指定的,但是在远程调用中,函数指针是不行的,因为两个进程的地址空间是完全不一样的。
所以在 RPC 中,所有的函数都必须有自己的一个 ID。这个 ID 在所有进程中都是唯一确定的。
客户端在做远程过程调用时,必须附上这个 ID。然后我们还需要在客户端和服务端分别维护一个函数和Call ID的对应表。
当客户端需要进行远程调用时,它就查一下这个表,找出相应的 Call ID,然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应函数的代码。
实现方式:服务注册中心。
要调用服务,首先你需要一个服务注册中心去查询对方服务都有哪些实例。Dubbo 的服务注册中心是可以配置的,官方推荐使用 Zookeeper。
实现案例:RMI(Remote Method Invocation,远程方法调用)也就是 RPC 本身的实现方式。
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_9.jpg
▲ RMI 架构图
Registry(服务发现):借助 JNDI 发布并调用了 RMI 服务。实际上,JNDI 就是一个注册表,服务端将服务对象放入到注册表中,客户端从注册表中获取服务对象。
RMI 服务在服务端实现之后需要注册到 RMI Server 上,然后客户端从指定的 RMI 地址上 Lookup 服务,调用该服务对应的方法即可完成远程方法调用。
Registry 是个很重要的功能,当服务端开发完服务之后,要对外暴露,如果没有服务注册,则客户端是无从调用的,即使服务端的服务就在那里。

5.3 序列化和反序列化

客户端怎么把参数值传给远程的函数呢?在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。
但是在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。
这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。
只有二进制数据才能在网络中传输,序列化和反序列化的定义是:

  • 1)将对象转换成二进制流的过程叫做序列化;
  • 2)将二进制流转换成对象的过程叫做反序列化。

这个过程叫序列化和反序列化。
同理,从服务端返回的值也需要序列化反序列化的过程。

5.4 网络传输

网络传输:远程调用往往用在网络上,客户端和服务端是通过网络连接的。
所有的数据都需要通过网络传输,因此就需要有一个网络传输层。网络传输层需要把 Call ID 和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端。
只要能完成这两者的,都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。
尽管大部分 RPC 框架都使用 TCP 协议,但其实 UDP 也可以,而 gRPC 干脆就用了 HTTP2。
TCP 的连接是最常见的,简要分析基于 TCP 的连接:通常 TCP 连接可以是按需连接(需要调用的时候就先建立连接,调用结束后就立马断掉),也可以是长连接(客户端和服务器建立起连接之后保持长期持有,不管此时有无数据包的发送,可以配合心跳检测机制定期检测建立的连接是否存活有效),多个远程过程调用共享同一个连接。
所以,要实现一个 RPC 框架,只需要把以下三点实现了就基本完成了:

  • 1)Call ID 映射:可以直接使用函数字符串,也可以使用整数 ID。映射表一般就是一个哈希表;
  • 2)序列化反序列化:可以自己写,也可以使用 Protobuf 或者 FlatBuffers 之类的;
  • 3)网络传输库:可以自己写 Socket,或者用 Asio,ZeroMQ,Netty 之类。

6、RPC 核心之网络传输协议

6.1 概述

上一节中说明了要实现一个 RPC,需要选择网络传输的方式。
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途_10.jpg
▲ 网络传输
在 RPC 中可选的网络传输方式有多种,可以选择 TCP 协议、UDP 协议、HTTP 协议。
每一种协议对整体的性能和效率都有不同的影响,如何选择一个正确的网络传输协议呢?首先要搞明白各种传输协议在 RPC 中的工作方式。

6.2 基于 TCP 协议的 RPC 调用

由服务的调用方与服务的提供方建立 Socket 连接,并由服务的调用方通过 Socket 将需要调用的接口名称、方法名称和参数序列化后传递给服务的提供方,服务的提供方反序列化后再利用反射调用相关的方法。
最后将结果返回给服务的调用方,整个基于 TCP 协议的 RPC 调用大致如此。
但是在实例应用中则会进行一系列的封装,如 RMI 便是在 TCP 协议上传递可序列化的 Java 对象。

6.3 基于 HTTP 协议的 RPC 调用

该方法更像是访问网页一样,只是它的返回结果更加单一简单。
其大致流程为:由服务的调用者向服务的提供者发送请求,这种请求的方式可能是 GET、POST、PUT、DELETE 等中的一种,服务的提供者可能会根据不同的请求方式做出不同的处理,或者某个方法只允许某种请求方式。
而调用的具体方法则是根据 URL 进行方法调用,而方法所需要的参数可能是对服务调用方传输过去的 XML 数据或者 JSON 数据解析后的结果,最后返回 JOSN 或者 XML 的数据结果。
由于目前有很多开源的 Web 服务器,如 Tomcat,所以其实现起来更加容易,就像做 Web 项目一样。

6.4 两种方式对比

基于 TCP 的协议实现的 RPC 调用,由于 TCP 协议处于协议栈的下层,能够更加灵活地对协议字段进行定制,减少网络开销,提高性能,实现更大的吞吐量和并发数。
但是需要更多关注底层复杂的细节,实现的代价更高。同时对不同平台,如安卓,iOS 等,需要重新开发出不同的工具包来进行请求发送和相应解析,工作量大,难以快速响应和满足用户需求。
基于 HTTP 协议实现的 RPC 则可以使用 JSON 和 XML 格式的请求或响应数据。
而 JSON 和 XML 作为通用的格式标准(使用 HTTP 协议也需要序列化和反序列化,不过这不是该协议下关心的内容,成熟的 Web 程序已经做好了序列化内容),开源的解析工具已经相当成熟,在其上进行二次开发会非常便捷和简单。
但是由于 HTTP 协议是上层协议,发送包含同等内容的信息,使用 HTTP 协议传输所占用的字节数会比使用 TCP 协议传输所占用的字节数更高。
因此在同等网络下,通过 HTTP 协议传输相同内容,效率会比基于 TCP 协议的数据效率要低,信息传输所占用的时间也会更长,当然压缩数据,能够缩小这一差距。

7、RPC的主要使用场景

RPC 主要用于公司内部的服务调用(准确地说是大型系统内部各模块、子系统间的调用,而不是提供给外界调用),性能消耗低,传输效率高,实现复杂。
相对而言,HTTP 主要用于对外的异构环境,比如:浏览器接口调用、App 接口调用、第三方接口调用等。
RPC 的主要使用场景尤其是大型的网站的情况下,内部子系统较多、接口非常多的情况下适合使用 RPC。
RPC 的使用场景一般来说有以下特点:

  • 1)长链接:不必每次通信都要像 HTTP 一样去 3 次握手,减少了网络开销;
  • 2)注册发布机制:RPC 框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作;
  • 3)安全性:没有暴露资源操作;
  • 4)微服务支持:就是最近流行的服务化架构、服务化治理,RPC 框架是一个强力的支撑。

8、小结一下

RPC(即远程调用)简单说就是发送一个请求给远程机器(一般来说就是分布系统内网中的机器),远程机器返回一个结果回来的过程。
为什么要这么做?因为单台服务器的性能远远不能满足现在互联网这个体量的用户的需求,就好比你去肯德基点个餐,餐台的服务员把薯条鸡腿汉堡的任务分给不同的人,然后收集起来给你的过程,餐台服务员就相当于调用远程服务。
但假如不这么做,点餐员直接做这些事情(又得点餐,又得炸薯条、炸鸡腿等等),两相比较,你就知道远程调用有什么好处了。
简单来说就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如比如不同的系统间的通讯,甚至不同的组织间的通讯。由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用。
当然,RPC有有点也有缺点,只有用在合适的场景,才能发挥它的最大威力。
RPC的主要优点有:

  • 1)提升系统可扩展性;
  • 2)提升系统可维护性和持续交付能力;
  • 3)实现系统高可。

RPC的主要缺点有:

  • 1)一个完善的RPC框架开发难度大、成本高 ;
  • 2)RPC框架调用成功率受限于网络状况;
  • 3)调用远程方法对初学者来说难度大。

作者:李金葵

来源:http://www.52im.net/thread-2620-1-1.html


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)