分布式微服务架构:原理与实战
上QQ阅读APP看书,第一时间看更新

3.1 RPC框架概述

3.1.1 RPC的定义

RPC(Remote Procedure Call,远程过程调用)是一种进程间的通信方式。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式地编码远程调用的细节,即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。目前,主流的平台都支持各种远程调用技术,以满足分布式系统架构中不同系统之间的远程通信和相互调用。

3.1.2 RPC核心组件

完整的RPC框架主要包含4个核心的组件:Client、Server、Client Stub以及Server Stub,具体如图3-1所示。

  • 客户端(Client):服务调用方。
  • 服务端(Server):服务提供者。
  • 客户端存根(Client Stub):存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。
  • 服务端存根(Server Stub):接收客户端发送过来的消息,将消息解包,并调用本地的方法。

图3-1 RPC核心组件图

3.1.3 RPC调用过程

RPC调用过程如图3-2所示。

图3-2 RPC调用过程

(1)客户端(Client)以本地调用方式调用服务(依赖服务接口,以接口的方式调用)。

(2)客户端存根(Client Stub)接收到调用请求后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制)。

(3)客户端通过Socket将消息发送到服务端。

(4)服务端存根(Server Stub)收到消息后对消息进行解码(将消息对象反序列化)。

(5)服务端存根(Server Stub)根据解码结果调用本地的服务(利用反射原理)。

(6)本地服务执行并将结果返回给服务端存根(Server Stub)。

(7)服务端存根(Server Stub)将返回结果打包成消息(将结果消息对象序列化)。

(8)服务端(Server)通过Socket将消息发送到客户端。

(9)客户端存根(Client Stub)接收到结果消息,并进行解码(将结果消息反序列化)。

(10)客户端(Client)得到最终结果。

无论是哪种类型的数据,最终都需要转换成二进制流在网络上进行传输,数据的发送方需要将对象转换为二进制流,而数据的接收方则需要把二进制流再恢复为对象。RPC的目标是把(2)、(3)、(4)、(7)、(8)、(9)这些步骤都封装起来,具体如图3-3所示。

图3-3 RPC框架原理图

3.1.4 RPC框架调用分类

RPC调用主要分为两种:同步调用和异步调用

  • 同步调用:客户端调用服务端方法,等待直到服务端返回结果或者超时,再继续自己的操作。
  • 异步调用:客户端调用服务端方法,不再等待服务端返回,直接继续自己的操作。
1. 同步调用

在分布式微服务架构中,一个业务的调用会跨N(N≥2)个服务进程,整个调用链路上的同步调用等待的瓶颈会由最慢(或脆弱)的服务决定。比如A-B-C这样一个调用链路,A同步调用B并等待返回结果,B同步调用C并等待返回结果,以此类推,就像一组齿轮链,级级传动,这很容易产生雪崩效应。若C服务挂了,则会导致前面的服务全部因为等待超时而占用大量不必要的线程资源。对于雪崩效应,常用解决方法有:使用超时策略和熔断器机制。

(1)超时策略:在一个服务调用链中,某个服务的故障可能会导致级联故障。调用服务的操作可以配置为执行超时,如果服务未能在这个时间内响应,就回复一个失败消息。然而,这种策略可能会导致许多并发请求到同一个操作被阻塞,直到超时期限届满。这些阻塞的请求可能会存储关键的系统资源,如内存、线程、数据库连接等。因此,这些资源可能会枯竭,导致需要使用相同的资源系统出现故障。设置较短的超时可能有助于解决这个问题,但是一个请求从发出到收到成功或者失败的消息需要的时间是不确定的。在分布式微服务架构下,我们需要根据成功调用一个服务调用链的平均时间来合理配置服务接口超时时间。

(2)熔断器机制:熔断器的模式使用断路器来检测故障是否已得到解决,防止请求反复尝试执行一个可能会失败的操作,从而减少等待纠正故障的时间,相对于超时策略更加灵活。熔断器机制在后续的章节中会有详细描述。

注 意


RPC的同步调用确保请求送达对方并收到对方响应,若没有收到响应,则框架抛出Timeout超时异常。这种情况下,调用方是无法确定调用是成功还是失败的,需要根据业务场景(是否可重入,幂等)选择重试和补偿策略。

2. 异步调用

RPC的异步调用意味着RPC框架不阻塞调用方线程,调用方不需要立刻拿到返回结果,甚至调用方根本就不关心返回结果。RPC的异步交互场景如图3-4所示。

图3-4 RPC异步交互原理

由图3-4可知,异步请求返回一个Future对象给调用方,以便调用方可以通过Future来获取返回值。有了Future机制,我们再来看一个具体实例,如图3-5所示。

图3-5 RPC异步交互具体实例

调用方通过服务网关(API Gateway)发起调用并等待结果,随后网关派发调用请求给后续服务,其主调用链路为A-B-C,其内部为异步调用,链路上不等待,最后由C返回结果给服务网关。其中,B又依赖两个子服务:B11和B22,B需要B11和B22的返回结果才能发起C调用,因此在支线上B针对B11和B22调用就需要是同步的。

注 意


在微服务架构下,大部分的服务调用都是同步调用,异步调用的使用场景:上游服务不会实时关注下游服务的调用结果,比如通过异步调用记录日志。通过异步调用在一定程度上可以提升服务性能,但不可滥用,否则会产生不可预料的结果。

3.1.5 RPC框架性能

影响RPC框架性能的因素如下:

  • 网络IO模型:在高并发状态下,阻塞式同步IO、非阻塞式同步IO或者多路IO模型对RPC服务器影响很大,特别是单位处理性能下对内存、CPU资源使用率的影响。
  • 基于的网络协议:RPC框架可选择的协议有HTTP协议、HTTP/2协议、TCP协议等。HTTP协议使用文本协议对传输内容进行编码,相对于采用二进制编码协议的TPC/IP协议码流更大。选择不同的协议对RPC框架的性能有一定的影响。目前没有采用UDP协议作为主要的传输协议。
  • 消息封装格式:消息封装格式的设计是影响RPC框架性能最重要的原因,这就是为什么几乎所有主流的RPC框架都会设计私有的消息封装格式。选择或者定义一种消息格式的封装要考虑的问题包括消息的易读性、描述单位内容时的消息体大小、编码难度、解码难度、解决半包/粘包问题的难易度。Dubbo消息体数据包含Dubbo版本号、接口名称、接口版本、方法名称、参数类型列表、参数、附加信息等。
  • Schema和序列化(Schema & Data Serialization):序列化、反序列化的时间,序列化后数据的字节大小直接影响RPC框架性能。

3.1.6 RPC框架与分布式服务框架

RPC框架的通信方式是点对点,即服务消费者与服务提供者是点对点通信的。点对点通信包括通信、序列化、反序列化以及协议等内容。分布式服务框架不仅具有RPC框架的特性,还包括以下特性:

  • 由多台服务器提供服务,具有负载均衡策略。
  • 服务自动注册、发布。
  • 服务治理。

当大规模的应用服务化之后,服务治理问题会慢慢暴露出来,纯粹的RPC框架服务治理能力都不健全,要解决这些问题,必须通过服务框架和服务治理来完成,单凭RPC框架无法解决服务治理问题。