从 RPC 到服务化框架设计

327次阅读  |  发布于3年以前

目前互联网系统都是微服务化,那么就需要 RPC 调用,因此本文梳理了从 RPC 基本框架协议到整个服务化框架体系建设中所包含的知识点,重点在于 RPC 框架 和 服务治理能力的梳理,本文定位于一个科普性质的文章,在于让大家了解一个全貌。

一、RPC 基本框架

1-1、RPC 基本框架

理解 RPC

RPC 的概念就是远程过程调用。我们本地的函数调用,就是 A 方法调 B 方法,然后得到调用的结果,RPC 就是让你像本地函数调用一样进行跨服务之间的函数调用。互联网发展到现在,我们都在讲微服务,服务都拆分为微服务了,那么相关依赖的调用,就会变成跨服务之间的调用,而他们之间的通信方式就是依靠 RPC。

RPC 基础结构(RPC 协议)

Nelson 的论文 Implementing Remote Procedure Calls 告诉我们, RPC 协议包括 5 个部分:

  1. Client
  2. Client-stub
  3. RPCRuntime
  4. Server-stub
  5. Server

当 Client 发起一个远程调用时,它实际上是调用本地的 Stub。本地 stub 负责将调用的接口、方法和参数,通过约定的协议规范进行编码,并通过本地的 RPCRuntime 进行传输,然后将数据包发送到网络上传输出去。当 Server 端的 RPCRuntime 收到请求后,交给 Server-Stub 进行解码,然后调用 Server 端的函数或者方法,执行完毕就开始返回结果,Server-Stub 将返回结果编码后,发送给 Client,Client 端的 RPCRuntime 收到结果,发给 Client-Stub 解码得到结果,返回给 Client。

这里面分了三个层次:

1-2、RPC 框架的重点

从 RPC 基础结构中,我们总结出 RPC 框架的重点,包括 4 部分,如下:

1-2-1、数据序列化

序列化就是将数据结构或对象转换成二进制的过程,也就是编码的过程,序列化后数据才方便进行网络传输;反序列化就是在序列化过程中所生成的二进制转换成数据结构或者对象的过程,将二进制转换为对象后业务才好进行后续的逻辑处理。

常见的序列化协议如下:

常见的 RPC 框架如 gRPC、Thrift、Dubbo、RPCX 、Motan 等会支持上述协议中的大部分,尤其是 ProtoBuf 和 JSON 。目前从性能上和使用广泛度上来看,现在一般推荐使用 ProtoBuf,当然很多自研的框架里面他们也会自己实现他们自己的序列化协议。

1-2-2、网络传输(网络通信)

在数据被序列化为二进制后就可以行网络传输了,网络传输就是我们的数据怎么传输到对方服务器上,目前来说,常见的通信传输方式包括 :TCP、UDP、HTTP(HTTP2.0)、QUIC 协议,TCP 是大部分框架都会默认支持的,额外这里要说明一下,RPCX 支持 QUIC 而 gRPC 支持 HTTP2.0。

QUIC(Quick UDP Internet Connection)是谷歌制定的一种互联网传输层协议,它基于 UDP 传输层协议,同时兼具 TCP、TLS、HTTP/2 等协议的可靠性与安全性,可以有效减少连接与传输延迟,更好地应对当前传输层与应用层的挑战。QUIC 在应用程序层面就能实现不同的拥塞控制算法,不需要操作系统和内核支持,这相比于传统的 TCP 协议,拥有了更好的改造灵活性,非常适合在 TCP 协议优化遇到瓶颈的业务。

1-2-3、RPC 调用方式

网络传输只是数据传输非常基础的一方面,从业务上来看,我们发起一次 RPC 调用,那么还需要 RPC 的调用方式,包括如下三大类:

1-2-4、服务治理

RPC 协议只是定义了 Client 与 Server 之间的点对点调用流程,包括 stub、通信协议、RPC 消息解析等部分。但是在实际应用中,远程过程调用的时候还需要考虑服务的路由、负载均衡、高可用等问题,而保障服务之间的调用就需要进行服务治理,服务治理基本就涵盖:服务注册和发现、限流、降级、熔断、重试、失败处理、负载均衡等各种服务治理策略。

到这里,RPC 框架的重点的 4 大部分就介绍完毕了,现在再来看看,常见的 RPC 框架:

1-3、常见 RPC 框架

RPC 框架就是在 RPC 协议的基础上,来完善一些偏向业务实际应用的功能,从而满足不同场景的业务诉求。综合来看,目前业界的 RPC 框架大致有两种不同的侧重方向,一种偏向于服务治理型,一种偏向于跨语言调用型。

1-3-1、服务治理型 RPC 框架

业界比较出名的服务治理型的 RPC 框架有 Dubbo、DubboX、Motan、RPCX 等。

服务治理型 RPC 框架的特点是功能丰富,提供高性能的远程调用以及服务发现、服务治理等功能;常用于微服务化的业务系统中,对于特定语言的项目可以十分友好的透明化接入,是当前业界的主流。但缺点是语言耦合度较高,跨语言支持难度较大。

1-3-2、跨语言调用型 RPC 框架

业界比较出名的跨语言调用型的 RPC 框架有 :

跨语言调用型 RPC 框架的重点是关注于服务的跨语言调用,能够支持我们常见的大部分的语言进行语言无关的调用,非常适合于为不同语言提供通用远程服务的场景,但这类框架没有服务发现、服务治理等机制,使用这些框架的时候需要我们自己来实现服务发现、服务治理等相关策略。

那么,跨语言调用指的是啥意思呢,具体是:客户端和服务端可以在各种环境中运行和相互通信,并且可以用框架支持的任何语言编写(参考 gRPC 官网中的一张图如下,比如 C++ 的服务可以调用 Ruby 的服务:)

1-3-3、常见 RPC 框架对比

二、通用的服务化框架设计

我们一般讲的微服务框架包含了 RPC 框架,微服务体系中最重要的就是 RPC 框架,并且是一般是偏向服务治理的 RPC 框架。微服务需要提供的核心能力包括:微服务架构中通讯的基础协议 RPC、服务发现与注册、负载均衡、容错、熔断、限流、降级、权限、全链路日志跟踪。

2-1、微服务框架的核心能力(服务治理策略)

2-1-1、服务注册与发现

微服务后,服务大量增加,因此我们一定要能够有一个合适的方案能够发现对方的所有服务,业界比较常见的服务发现的组件如 zookeeper、etcd、consul 等,基本原理就是先将自己的服务列表到注册中心注册,然后再提供服务发现能力。

服务发现机制有服务端发现和客户端发现两种实现方式:

2-1-2、服务路由 & 负载均衡

服务路由和服务发现紧密相关,服务路由一般不会设计成一个独立运行的系统,通常情况下是和服务发现放在一起实现的。在服务路由中,最关键的能力就是负载均衡。我们一般常见的负载均衡算法有:随机路由、轮询路由、hash、权重、最小压力路由、最小连接数路由、就近路由等。

从业界来看,负载均衡的实现方案一般可以分为三类:

2-1-3、服务容错

负载均衡和容错是服务高可用的重要手段。服务容错的设计有个基本原则,就是“Design for Failure”。常见的服务容错策略如请求重试、限流、降级、熔断、隔离

超时与重试

超时机制算是一种最常见的服务容错模式了,我们发起的任何请求调用,都不可能无限等待,对方服务可能因为各种原因导致请求不能及时响应,因此超时机制是最基础并且是必须的。超时可能有网络超时、也可能是对方服务异常等多种情况。

重试一般和超时模式结合使用,适用于对于下游服务的数据强依赖的场景,通过重试来保证数据的可靠性或一致性,不强依赖的场景不建议使用。在对方服务超时之后,可以根据情况进行重试(对方服务返回异常就不要重试了)。但是一定注意,重试不能盲目重试,在重试的设计中,我们一般都会引入,Exponential Backoff 的策略,也就是 "指数级退避",每一次重试所需要的 sleep 时间都会指数增加,否则可能会导致拖累到整个系统。

服务限流

限流和降级用来保证核心服务的稳定性;限流是指限制每个服务的最大访问量、降级是指高峰期对非核心的系统进行降级从而保证核心服务的可用性

限流的实现方式:

分布式限流和单机限流:

容错降级

容错降级可以分为三大类,从小到大依次是:

熔断

熔断设计来源于日常生活中的电路系统,在电路系统中存在一种熔断器(Circuit Breaker),它的作用就是在电流过大时自动切断电路。熔断器一般要实现三个状态:闭合、断开和半开,分别对应于正常、故障和故障后检测故障是否已被修复的场景。

熔断设计的一般思路是,在请求失败 N 次后在 X 时间内不再请求,进行熔断;然后再在 X 时间后恢复 M% 的请求,如果 M% 的请求都成功则恢复正常,关闭熔断,否则再熔断 Y 时间,依此循环。

在熔断的设计中,根据 Netflix 的开源组件 hystrix 的设计,最重要的是三个模块:熔断请求判断算法、熔断恢复机制、熔断报警:

隔离

隔离,也就是 Bulkheads 隔板的意思,这个术语是用在造船上的,也就是船舱里防漏水的隔板。

在服务化框架的隔离设计中,我们同样是采用类似的技术来让我们的故障得到隔离。因此这里的重点就是需要我们对系统进行分离。一般来说,有两种方式,一种是以服务的类型来做分离,一种是以用户来做分离。

2-1-4、集群容错

在分布式场景下,我们的服务在集群中的都是有冗余的,一个是为容错,一个是为了高并发,针对大量服务实例的情况下,因此就有了集群容错的设计。集群容错是微服务集群高可用的保障,它有很多策略可供选择,包括:

2-2、微服务框架的基础能力

服务监控和告警

开源代表作:Prometheus + Grafana,遵循 OpenMetrics 规范,基本数据格式分为 Gauge、Count、Summary、Histogram

分布式服务 Tracing 跟踪系统

目前有两种协议规范:

配置中心

配置中心用来管理大量微服务之间的业务配置,并且是中心化的统一配置中心来进行管理。

远程日志

远程日志组件的代表作是 ELK 系统:Elasticsearch, Logstash, Kibana。

在微服务架构中,一个客户端请求的接入,往往涉及到后端一系列服务的调用,如何将这些请求串联起来?业界常用的方案是采用全局流水号【traceID】串联起来。通过全局流水号【traceID】,从日志里面可以拉出整条调用链路。

这里关于整体链路又和 分布式服务 Tracing 跟踪系统 关联起来,Tracing 可以知道整体链路的请求质量,远程日志+ traceID 可以知道整体链路的日志详情。

2-3、微服务框架依托的自动化运维能力

微服务框架建设 ok 之后,那么大量服务怎么运维,这就依托自动化运维能力,包括如下几个方面:

业界目前一般采用容器平台,微服务框架 + K8s 容器平台 是当今互联网业务的黄金标准

2-4、小结:自己搭建一个服务化框架的思路

自己搭建一个服务化框架的思路:

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8