本文引用了后端技术指南针公众号“浅谈RPC那些事儿1”和即时通讯网的“即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途”两篇文章的部分内容。
1、引言
经常有开发者在纠结怎么开发IM集群,虽然真正的使用人数,可能用个人电脑单机都能支撑。
你也许会说,明明不需要用到IM集群,干吗要自找麻烦?答曰:“老板说这个得有!”、“万一产品做成了,用户量达到百万、千万级呢?”,各种回答,反此种种。总之,IM集群就是得整一个(先甭管用不用的上...)。
当然,玩笑归玩笑,真正要做到可投入到生产级别的IM集群系统,难度还是相当大的。必竟IM这种长连接应用相比传统Http这种短连接应用太不标准。
我们以一个典型的IM聊天消息传输为例:
假设存在两个正在聊天的用户(用户A和用户B),当A连接的是IM集群中的IM实例1、B连接的是IM集群中的IM实例2,此时当用户A向用户B发送一条聊天消息时,这条消息应该如何传递呢?
我们梳理一下上面这个例子的消息流转过程:
1)IM聊天消息首先会由用户A发往IM实例1; 2)IM实例1会将此条消息转交给IM实例2; 3)IM实例2会将此条消息最终投递给连接在本实例上的用户B。
如上述流程所示,这就是一个IM集群系统中典型的聊天消息投递过程。
那么,这其中涉及到一个关键步骤:即第2)步中如何实现“IM实例1会将此条消息转交给IM实例2”?
此时,RPC技术出场了!
▲ 上图是个典型的分布式IM架构,注意中间的“RPC通信”字样(本图引用自《基于Go的马蜂窝旅游网分布式IM系统技术实践》)
本文将以通俗易懂的白话形式,帮你快速理解IM集群中的关键技术——RPC。
推荐阅读:即时通讯网的另一篇RPC基础知识文章也值得一读《即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途》。
2、相关文章
▼ 以下两篇文章有助于您对RPC和IM集群有个初步的概念:
《即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途》(推荐) 《即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?》
▼ IM开发干货系列文章(本文是其第24篇):
《IM消息送达保证机制实现(一):保证在线实时消息的可靠投递》 《IM消息送达保证机制实现(二):保证离线消息的可靠投递》 《如何保证IM实时消息的“时序性”与“一致性”?》 《IM单聊和群聊中的在线状态同步应该用“推”还是“拉”?》 《IM群聊消息如此复杂,如何保证不丢不重?》 《一种Android端IM智能心跳算法的设计与实现探讨(含样例代码)》 《移动端IM登录时拉取数据如何作到省流量?》 《通俗易懂:基于集群的移动端IM接入层负载均衡方案分享》 《浅谈移动端IM的多点登陆和消息漫游原理》 《IM开发基础知识补课(一):正确理解前置HTTP SSO单点登陆接口的原理》 《IM开发基础知识补课(二):如何设计大量图片文件的服务端存储架构?》 《IM开发基础知识补课(三):快速理解服务端数据库读写分离原理及实践建议》 《IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token》 《IM群聊消息的已读回执功能该怎么实现?》 《IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?》 《IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列》 《一个低成本确保IM消息时序的方法探讨》 《IM开发基础知识补课(六):数据库用NoSQL还是SQL?读这篇就够了!》 《IM里“附近的人”功能实现原理是什么?如何高效率地实现它?》 《IM开发基础知识补课(七):主流移动端账号登录方式的原理及设计思路》 《IM开发基础知识补课(八):史上最通俗,彻底搞懂字符乱码问题的本质》 《IM的扫码登功能如何实现?一文搞懂主流应用的扫码登陆技术原理》 《IM要做手机扫码登陆?先看看微信的扫码登录功能技术原理》 《IM开发基础知识补课(九):想开发IM集群?先搞懂什么是RPC!》(本文)
如果您是IM开发初学者,强烈建议首先阅读《新手入门一篇就够:从零开发移动端IM》。
3、正文概述
限于篇幅原因,本文不会深入展开RPC的底层技术原理,会尽量用通俗白话的方式对概念性的东西进行讲解。
通过本文你将主要了解到以下内容:
1)什么是RPC; 2)为什么需要RPC; 3)RPC的重要组件; 4)常见RPC框架和各自特点。
4、什么是RPC?
RPC 是1984年代由 Andrew D. Birrell & Bruce Jay Nelson 提出的(见二位大佬的论文《Implementing Remote Procedure Calls》),所以它并不是最近才有的技术概念。
关于RPC的介绍,正经的资料上大概是这样介绍的:
RPC(Remote Procedure Call)远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
大白话理解RPC就是:RPC让你用别人家的东西就像自己家的一样。
看得我似懂非懂,于是我不得不问几个问题:
1)为啥要用别人家的东西——请求其他服务); 2)我怎么可以借到别人家的东西——其他服务调用; 3)要是借用的话哪种形式更好——确定一个合适的调用方法); 4)怎么让我用别人东西像自己的一样——屏蔽底层细节透明通信)。
在解答这些问题之前,我们必须达到一个共识问题:RPC只是一种通信模式,和http并不冲突对立,相反http可以作为RPC传输数据的一种协议,把RPC当作一种模式和思想,我们才能更好地理解它。
更严谨的RPC基础知识介绍,请阅读:《即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途》。
5、为什么需要RPC?
以大家最熟悉的电商系统为例,这样规模的分布式系统,需要拆分出用户服务、商品服务、优惠券服务、支付服务、订单服务、物流服务、售后服务等等。这些服务之间都相互调用,这时内部调用最好使用 RPC ,同时每个服务都可以独立部署,独立上线。
也就说当我们的项目太大,需要解耦服务,扩展性强、部署灵活,这时就要用到 RPC ,这主要是解决了分布式系统中,服务与服务之间的调用问题。
▲ 上图中的分布系统内部,就是用RPC实现的(本图引用自《从新手到架构师,一篇就够:从100到1000万高并发的架构演进之路》)
对于IM集群这样的分布式系统来说,不同IM实例间的用户聊天消息,就是通过RPC进行流转的。
6、为什么不直接使用HTTP,而要搞RPC?
在日常业务中我们可以把功能封装成静态库、动态库、sdk、独立服务等,最常见也最方便的还是HTTP这种形式的调用。
HTTP服务把需要提供的服务暴露成接口(也就是通常所说的http rest接口啦),使用方直接按约定的HTTP方法和URI进行数据交互。
我们都知道HTTP协议是应用层协议,是个非常标准的协议,在HTTP协议之下还有网络层、传输层、数据链路层等,一个数据包packet除了净荷payload之外还有很多header,由于标准和通用性的设计目标也使得HTTP一次数据交互真正传输的payload只是其中一部分。
HTTP是我们用的最多最熟悉的交互模式,在系统内部各个服务之间接口较少,交互不多的情况下工作得还不错。
但如果在内部系统调用很复杂的前提下,HTTP调用的效率和安全性就不那么理想了。
以IM系统为例,单个IM实例的吞吐效率至少可以达到几万甚至数十万QPS,使用HTTP这种短连接(调用时建立socket连接,完成后释放连接)方式显的相当低效(每次调用都要重新经历TCP的3次握手、4次挥手过程),在分布式的情况下势必拉低整个IM集群的吞吐效率。而对于RPC,这种socket长连接方式对于高性能场景来说,效果是显而易见的。
更重要的是面对众多的服务我们需要的不仅仅是一个通信方式,而是一个内部服务的管理系统,这也就是我们今天说的RPC框架。注意:RPC是一种模式策略和框架,并不是单纯的通信协议。
题外话:实际上,HTTP在RPC系统中,并不是个你死我活的关系,必竟HTTP只是个通信协议,而HTTP有某些性能要求不敏感的场景来说,是完全可以作为RPC的具体实现协议之一来使用的。
7、RPC的调用过程是什么样的?
▲ 典型的RPC调用过程
如上图所示,一个典型的RPC调用过程是这样(过程序号对应上图中的数字):
1)客户端(client)以本地调用方式调用服务;
2)客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);
3)客户端通过 sockets 将消息发送到服务端;
4)服务端存根(server stub)收到消息后进行解码(将消息对象反序列化);
5)服务端存根(server stub)根据解码结果调用本地的服务;
6)本地服务执行并将结果返回给服务端存根(server stub);
7)服务端存根(server stub)将返回结果打包成消息(将结果消息对象序列化);
8)服务端(server)通过 sockets 将消息发送到客户端;
9)客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);
10)客户端(client)得到最终结果。
RPC的作用,其实就是要把上述2、3、4、7、8、9 这些步骤都封装起来。是不是很神奇?
8、关于HTTP和RPC的一些争议
HTTP和RPC是两个很容易混淆的概念,对于刚开始接触RPC的人来说,通常都会困惑:有HTTP了为什么还要用RPC?
在知乎上看到了这个很有趣的问题:《既然有http请求,为什么还要用rpc?》
其中一个大佬的回答感觉很有意思:
换个角度来说:HTTP 与 RPC 的关系就好比普通话与方言的关系。要进行跨企业服务调用时,往往都是通过 HTTP API,也就是普通话,虽然效率不高,但是通用,没有太多沟通的学习成本。但是在企业内部还是 RPC 更加高效,同一个企业公用一套方言进行高效率的交流,要比通用的 HTTP 协议来交流更加节省资源。整个中国有非常多的方言,正如有很多的企业内部服务各有自己的一套交互协议一样。虽然国家一直在提倡使用普通话交流,但是这么多年过去了,你回一趟家乡探个亲什么的就会发现身边的人还是流行说方言。
如果再深入一点说,普通话本质上也是一种方言,只不过它是官方的方言,使用最为广泛的方言,相比而言其它方言都是小语种,小语种之中也会有几个使用比较广泛比较特色的方言占比也会比较大。这就好比开源 RPC 协议中 Protobuf 和 Thrift 一样,它们两应该是 RPC 协议中使用最为广泛的两个。
总之:RPC是一种编程模式和概念,并不是非常具体的一种技术,实际上和HTTP没有明确的冲突,HTTP可以作为RPC传输协议,原因还是RPCpid际上是一种内部服务框架而不是一个具体的通信协议,它可以涉及服务注册、服务治理、服务发现、熔断机制、负载均衡等。
9、典型的RPC框架
一个典型RPC框架中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进行网络传输和序列化。
▲ 一个典型的 RPC 架构原理图(本图引用自《即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途》)
▲ 著名RPC框架Dubbo的架构图(本图引用自《即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途》)
一个 RPC 最重要的功能模块,就是上图中的”RPC 协议”部分:
其中的序列化和反序列化的意思是:
1)序列化:将数据结构或对象转换成二进制串的过程;
2)反序列化:将序列化中所生成的二进制串转换成数据结构或者对象的过程。
在网络消息传输中可以基于TCP、UDP、http来实现,各自都有各自的特点。
基于 TCP 实现的 RPC 调用,能够灵活对协议字段进行定制,减少网络开销提高性能,实现更大的吞吐量和并发数,但要关注底层细节,在进行数据解析时更加复杂一些(比如最受欢迎的Protobuf的使用)。
基于 HTTP 实现的 RPC 可以使用 JSON 和 XML 格式的请求或响应数据,解析工具很成熟,在其上进行二次开发会非常便捷和简单。但是 HTTP 是上层协议,所占用的字节数会比使用 TCP 协议传输所占用的字节数更高。
对于其他部分,本文不再展开。
10、市面上常见的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 等。
下面重点介绍当前最流行的三种RPC框架主要特点:
1)gRPC:是 Google 公布的开源软件,基于最新的 HTTP 2.0 协议,并支持常见的众多编程语言。RPC 框架是基于 HTTP 协议实现的,底层使用到了 Netty 框架的支持;
2)Thrift:是 Facebook 的开源 RPC 框架,主要是一个跨语言的服务开发框架。用户只要在其之上进行二次开发就行,应用对于底层的 RPC 通讯等都是透明的。不过这个对于用户来说需要学习特定领域语言这个特性,还是有一定成本的;
3)Dubbo:是阿里集团开源的一个极为出名的 RPC 框架,在很多互联网公司和企业应用中广泛使用。协议和序列化框架都可以插拔是极其鲜明的特色。
11、本文小结
小结一下,简单地理解RPC就是:
RPC 就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯)。
RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式)。
RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。
12、参考资料
[1] 什么是 RPC 框架
[2] 谁能用通俗的语言解释一下什么是 RPC 框架?
[3] 浅谈RPC那些事儿[1]
[4] 即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途
[5] 即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?