当前位置:   article > 正文

RPC:RPC的通信流程_不同系统之间的rpc调用

不同系统之间的rpc调用

为什么要学习RPC

现在的大规模系统中,经常会将它们做一些服务化拆分,这个过程也叫做微服务,而微服务会带来一些新的问题,主要有两个:

  • 服务拆分单独部署之后,引入的服务跨网络通信的问题
  • 在拆分成多个小服务之后,服务如何治理的问题

第一个问题可以用RPC框架来解决。只要涉及到网络通信,我们就可能用到RPC,RPC是解决分布式系统通信的一大利器

  • RPC 最大的特点就是可以让我们像调用本地一样发起远程调用。发起调用请求的那一方叫做调用方,被调用的一方叫做服务提供方。
  • RPC 的全称是 Remote Procedure Call,即远程过程调用。指的是通过网络,调用另一台计算机上部署服务的技术。
  • 简单解读字面上的意思,远程肯定是指要跨机器而非本机,所以需要用到网络编程才能实现。
    • 分布式系统中的网络通信一般都TCP协议或者HTTP协议
    • RPC是一个远程调用,那就肯定需要通过网络来传输数据,并且RPC常用于业务系统之间的数据交互,需要保证其可靠性,所以RPC一般默认采用TCP来传输。我们常用的HTTP协议也是建立在TCP上面的。
  • 但是不是只要通过网络通信访问到另一台机器的应用程序,就可以称之为RPC调用了?显然并不够。
  • 我理解的RPC是帮助我们屏蔽网络编程细节,实现调用远程方法就跟调用本地方法一样的体验,我们不需要因为这个方法是远程调用就需要编写很多核业务无关的代码。
    • 网络通信说起来简单,但是实际上是一个非常复杂的过程,这个过程包括:对端节点的查找、网络连接的建立、传输数据的编码解码以及网络连接的管理等,每一项都很复杂
    • 你可以想象一下,在搭建一个复杂的分布式系统过程中,如果开发人员在编码时要对每个涉及到网络通信的逻辑都进行一系列复杂编码,这将是多么恐怖的事儿。所以说,网络通信是搭建分布式系统的一大难题,必须足够的重视
    • 而RPC对网络通信的整个过程做了完整包装,在搭建分布式系统时,它会使得网络通信逻辑的开发变得非常简单,同时也会让网络通信变得更加安全可靠

RPC 的作用就是体现在这样两个方面

  • 屏蔽远程调用跟本地调用的区别,让我们感觉就是调用本地方法
  • 隐藏底层网络通信的复杂性,让我们更专注于业务逻辑

RPC VS Web Service

  • 比如说Web Service就是RPC的一种实现方式。它的优势是使用HTTP+SOAP协议,保证了调用可以跨语言,跨平台。只要你支持HTTP协议,可以解析XML,那么就能够使用Web Service。但是由于它使用XML封装数据,数据包比较大,所以性能比较差。
  • 也就是说,RPC并不是互联网时代的产物,也不是服务化之后才衍生处理的技术,而是一种规范,只要是封装了网络调用的细节,能够实现远程调用其他服务,就可以算作一种RPC技术了。

那么项目在使用了RPC框架之后,会发生什么变化呢?

在性能上的变化是不可忽视的。举个例子:

  • 比如当前系统中,商品详情页面需要商品数据、评论数据以及店铺数据,如果在一体化架构中,就只需要从商品库、评论库和店铺库获取数据就可以了,不考虑缓存的情况下游三次网络请求
  • 但是,如果独立出商品服务,评论服务和店铺服务之后,那么就需要分布调用这三个服务,而这三个服务又会分布调用各自的数据库,这就是六次网络请求。如果你服务拆分的更细粒度,那么多出的网络调用就会更多,请求的延迟就会更长,而这就是你为了提升系统的扩展性,在性能上付出的代价。
  • 比如说,当前系统QPS已经达到了2万/秒,在做了服务化拆分之后,由于我们把业务逻辑,都拆分到了单独部署的服务中,那么假设你在完成一次完整的请求时,需要调用4~5次服务,计算下来,RPC服务需要承载大概10万/秒的请求。
    在这里插入图片描述

那么,我们要如何优化RPC的性能,从而尽量减少网络调用对性能的影响呢?(也就是说,RPC框架对性能的要求是非常高的)

RPC通信流程

一次完整的RPC过程如下:

  • 在一次RPC调用过程中,客户端首先会将调用的类名、方法名、参数名、参数值等信息,序列化成二进制流
    • 为什么要序列化?
      • 网络传输的数据必须是二进制数据,但调用方请求的出入参数都是对象。
      • 对象是肯定没有方法直接在网络中传输的,系统提前把它转换成可传输的二进制,并且要求转换算法是可逆的。
      • 这个过程一般叫做序列化
  • 然后客户端将二进制流,通过网络发送给服务端
  • 服务端接收到二进制流之后,将它反序列化,得到需要调用的类名、方法名、参数名和参数值,在通过动态代理的方式,调用对应的方法得到返回值
    • 服务提供方从TCP通道中收到二进制数据,那如何知道一个请求的数据到哪里结束,是一个什么类型的请求呢?
      • 可以建立一些“指示牌”,并在上面标明数据包的类型和长度,这样就可以正确解析数据了。确实可以,并且我们把数据格式的约定内容叫做“协议”。大多数协议会分成两部分,分别是数据头和消息体。数据头一般用于身份识别,包括协议标识、数据大小、请求类型、序列化类型等信息;消息体主要是请求的业务参数信息和扩展属性等。
      • 根据协议格式,服务提供方就可以正确的从二进制数据中分隔出不同的请求来,同时根据请求类型和序列化类型,把二进制的消息体还原成请求对象。这个过程叫做“反序列化”
      • 服务提供方在根据反序列化出来的请求对象找到对应的实现类,完成真正的方法调用,然后把执行结果序列化后,回写到对应的TCP通道里面。调用方获取到应答的数据包之后,再反序列化到应答对象,这样调用方就完成了一次RPC调用
    • 另外,为了不在再让研发人员需要手动写代码去构造请求、调用序列化,并进行网络调用,引入了动态代理技术,以屏蔽到RPC细节,让使用方只需要关注业务接口,像调用本地一样来调用远程
      • 类似Spring中的API,采用动态代理技术,通过字节码增强对方法进行拦截增强,以便于增加需要的额外处理逻辑。
  • 服务端将返回值序列化,再通过网络发送给客户端
  • 客户端对结果反序列化之后,就可以得到调用的结果了

在这里插入图片描述
从上图中国可以看到,有网络传输的过程,也有奖请求序列化和反序列化的过程。所以,如果要提升RPC框架的性能,需要从网络传输和序列化两方面来优化。

怎么做呢?需要做的是:

  • 选择合适的网络模型,有针对性的跳转网络参数,以优化网络传输性能
    • 在网络传输优化中,我们第一个要做的是选择一种高性能的IO模型,一般是非阻塞IO + 多路复用。
    • 可能还需要一些网络参数的调优:
      • 这里有一些经验值的推荐,比如将tcp_nodelay设置为true
      • 也有一些参数需要在实际运行中调试,比如接收缓存和发送缓冲区的大小,客户端连接请求缓冲队列的大小(back log)等等;
  • 选择合适的序列化过程
    • 通常所说的序列化,是将传输对象转换成二进制串的过程,而反序列化是将二进制串转换成对象的过程。
    • 从上面的RPC调用过程中可以看到,一次RPC调用需要经历两次数据序列化的过程,和两次数据反序列化的过程,可见它们对于RPC的性能影响是很大的
    • 序列化协议要根据具体业务选择。如果对性能要求不高,可以选择 JSON,否则可以从Thrift 和 Protobuf 中选择其一。

在选择序列化的时候需要考虑哪些因素呢

  • 首先要考虑的肯定是性能。性能包括时间开销和空间开销:时间开销就是序列化和反序列化的速度;空间开销就是序列化后的二进制串的大小,过大的二进制串也会占据传输带宽,影响传输效率
  • 其次,需要考虑它是否可以跨平台、跨语言。这一点也非常重要,因为一般的公司的技术体系都不是单一的,使用的语言也不是单一的,如果你的RPC框架中传输的数据只能被一种语言解析,那么这无疑限制了框架的使用
  • 最后,扩展性也是需要考虑的重点问题。如果扩展性不好,那么可能会出现对象增加了一个字段就造成传输协议不兼容,导致服务调用失败

综合上面的几个考虑点,序列化备选方案主要有以下几种:

  • 首先是,它起源于 JavaScript,是一种最广泛使用的序列化协议,它的优势简单易用,人言可读,同时在性能上相比 XML 有比较大的优势。
  • Thrift 和 Protobuf 都是需要引入 IDL(Interface description language)的,也就是需要按照约定的语法写一个IDL文件,然后通过特定的编译器将它转换成各语言对应的代码,从而实现跨语言的特点。
    • Thrift 是 Facebook 开源的高性能的序列化协议,也是一个轻量级的 RPC 框架
    • Protobuf 是谷歌开源的序列化协议
    • 它们的共同特点是,无论在空间上还是时间上都有着很高的性能,缺点就是由于 IDL 存在带来一些使用上的不方便。

那么,如何选择这几种序列化协议呢?建议

  • 如果对于性能要求不高,在传输数据占用带宽不大的场景下,可以使用 JSON 作为序列化协议;
  • 如果对于性能要求比较高,那么使用 Thrift 或者 Protobuf 都可以。而 Thrift 提供了配套的 RPC 框架,所以想要一体化的解决方案,你可以优先考虑 Thrift;
  • 在一些存储的场景下,比如说你的缓存中存储的数据占用空间较大,那么可以考虑使用protobuf代替json,作为存储数据的序列化方式

RPC在架构中的位置

如刚才所讲,RPC 是解决应用间通信的一种方式,而无论是在一个大型的分布式应用系统还是中小型系统中,应用架构最终都会从“单体”演进成“微服务化”,整个应用程序会被拆分成多个不同功能的应用,并把它们部署在不同的服务中,而应用之间会通过RPC进行通信,可以说RPC对应的是整个分布式应用程序,是“经络”一样的存在

但是拆分完的系统怎么保持跟未拆分前的调用方式一样呢?我们总不能因为架构升级,就把所有的代码都推倒重写一遍吧。

RPC框架能够帮助我们解决系统拆分后的通信问题,并且能让我们像调用本地一样去调用远程方法。利用RPC我们不仅可以很方便的将应用程序从“单体”演进成“微服务”,而且还能解决实际应用开发过程中的效率低下、系统耦合等问题,这样可以使得我们的系统架构整体清晰、健壮,应用可运维度增强

当然 RPC 不仅可以用来解决通信问题,它还被用在了很多其他场景,比如:发 MQ、分布式缓存、数据库等

总结

RPC提供了一种透明调用机制,让使用者不必显示的区分本地调用和远程调用。RPC 虽然可以帮助开发者屏蔽远程调用跟本地调用的区别,但毕竟涉及到远程网络通信,所以这里还是有很多使用上的区别,比如:

  • 调用过程中超时了怎么处理业务?
  • 什么场景下最适合使用 RPC?
  • 什么时候才需要考虑开启压缩?
本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/145687
推荐阅读
相关标签
  

闽ICP备14008679号