微服务架构之 —— RPC框架

RPC简介

  • RPC是什么

    Remote Procedure Call,远程过程调用。

    首先来说本地方法调用,假设在main方法中调用一个本地的方法multiply(同一个进程内的方法调用)。无非是做了内存寻址和一些堆栈操作。

    而假设main方法和multiply方法不在同一个进程中,两者则通过RPC的方式进行调用(通信)。由于是跨网络通信,需要考虑将方法调用和参数,如何变成网络上可传输的二进制流(涉及到参数和结果的序列化,反序列化,socket网络编程等细节)。而这就是RPC框架的意义,它帮助开发人员,屏蔽了网络编程和网络传输的细节,让开发人员像调用本地方法一样,调用远程方法。

    需要注意,RPC并不是一种具体的技术框架,而是一种技术思想

    广义上讲,任何通过网络,远程调用另一个进程中的方法的技术,都可以称为RPC。

    RPC并不是一个新的概念,也不是和微服务同一个时代的概念。

    在微服务之前,就有很多可以称之为RPC的技术理念。比如RMI,WebService。

    • RMI

      Remote Method Invocation。比较古老。是EJB的通信基础,是实现RPC的一套Java Api,依赖JVM,所以仅支持Java应用之间的方法调用,不能实现跨语言调用

      微服务架构之 —— RPC框架_第1张图片

    • WebService

      跨语言,跨平台。因为WebService底层依赖的是HTTP和XML,这两者都是语言无关的。

      微服务架构之 —— RPC框架_第2张图片

  • RPC有哪些具体的例子

    随着时代的发展,越来越多优秀的RPC框架进入我们的视野,如

    • gRPC:谷歌开源的高性能RPC框架。基于HTTP2协议,以protobuf作为序列化协议。使得其具有很好的跨平台特性。
    • dubbo:阿里开源的RPC框架,已捐赠给Apache
    • Finagle:twitter的RPC框架
    • Thrift:Facebook的RPC框架
    • Tars:腾讯的RPC框架
  • 引入RPC后会给项目带来什么

    当我们引入RPC框架,并对整体系统进行了微服务化的拆分。会导致:

    • 提升系统的扩展性(优点)

    • 提升了系统的可维护性(优点)

    • 系统变得复杂(缺点)

      引入新的组件,导致在调试代码,维护系统时会变得更加复杂

    • 跨网络调用可能会增加性能开销(缺点)

    所以,引入RPC框架对系统进行微服务化的拆分时,一定要综合扩展性,维护性,复杂度,成本,性能等多方面的考虑。需要慎之又慎。

RPC基本原理

RPC主要是为了解决服务的远程调用问题,即客户端和服务器之间的通信问题。当设计一款RPC框架时,主要有如下几个方面需要考虑

  • 通信协议
  • 序列化
  • IO模型

RPC调用的通信过程是怎样的?

  1. 首先,客户端和服务端需要约定一种通信协议,按照此种协议来组织数据
  2. 在组装数据时,需要将数据序列化为二进制流,以便在网络上进行传输
  3. 随后,客户端组织好方法名和参数等数据,将二进制流通过网络进行发送
  4. 服务端接收到数据后,按照通信协议解析数据,并把二进制流反序列化为方法名和参数
  5. 服务端执行业务逻辑,得到结果后,将结果以同样的方式,返回给客户端

这样就完成了一次RPC通信

通信协议

需要注意处理TCP粘包的问题。解决方案通常有

  • 发送和接收定长的数据(会浪费网络带宽)
  • 在数据包中额外携带长度信息(需要一个固定的空间来传输长度信息)
  • 使用特定的magic number作为数据的分界(无法判断某个数据包是否传输完)

通常使用第2种方式,或者第2和第3种结合的方式,来设计通信协议。

序列化

序列化,是将数据二进制流进行相互转换的方式。

每次RPC调用,都会涉及到2次序列化和2次反序列化的过程(共4次)。

所以,选择序列化方式时需要注意,要避免让序列化成为整个RPC通信的性能瓶颈。在选择序列化方式时,需要关注:

  • 性能

    (反)序列化执行的耗时

  • 序列化后的包的大小

    因为网络带宽是一定的,选择压缩比较高的序列化方式,可以减少高并发下RPC通信对带宽的占用

  • 可读性

    类似json,xml,是可读性较强的序列化方式,它们序列化后的结果都是文本。良好的可读性,主要是便于调试和问题排查。

需要在这3者之间做取舍和权衡。

常见的序列化方式

  • java serializable(不能跨语言)
  • json,xml(可读性较好,通用性高,但体积较大)
  • hessian,protobuf,thrift(二进制的序列化,性能高,序列化后的包体积小)
IO模型

常见的IO模型,如下

  • 阻塞IO
  • 非阻塞IO
  • 多路复用IO(对应Java的NIO)
  • 信号驱动IO
  • 异步IO(Java的AIO)

通常使用较多的是多路复用IO。异步IO还不够成熟

服务治理

目前业界开源的RPC框架中,更多的都是属于服务框架,而不是单纯的RPC框架。

服务框架和RPC框架的区别在于:RPC框架只是简单的解决通信问题,而服务框架,除了解决通信问题,还需要解决服务管理的问题。

所以我们需要了解,服务治理需要解决什么问题,以及它是如何解决的。

服务治理有哪些方面?

  • 服务注册与发现
  • 服务追踪(分布式追踪)
  • 配置管理
  • 服务熔断,降级,分流
  • 服务负载均衡
  • 服务监控
注册中心

过程

  • 服务启动时向注册中心注册服务节点地址
  • 客户端从注册中心获取服务的地址列表
  • 有新的服务注册上来后,客户端可以得到通知
  • 有服务出现故障或者关闭时,客户端也可以得到通知
  • 服务节点间隔固定时间向注册中心发送心跳,表明健康状况

优点

  • 客户端无需重启就能感知服务节点的动态变化
  • 可以通过为服务节点增加权重,来辅助负载均衡

选型

  • 分布式一致性协调系统

    zookeeper,etcd,consul

    可以当作注册中心使用,但可能功能不是很齐全,比如无法对服务做健康检测

  • 开源的,专门的注册中心

    nacos(阿里开源),eureka(springCloud),vintage(微博使用)

    功能比较完善

服务追踪

分布式追踪系统,主要是用于解决分布式环境下的问题排查的问题。

当我们把一个系统,拆成多个微服务之后,一次来自客户端的请求,可能会引发微服务之间的十几次,甚至几十次请求,才能得到最终的结果。

比如系统的响应时间变慢了,或者系统出了一些问题,那么我们需要知道是具体哪个微服务导致的,这就需要对整个调用链进行追踪。

分布式追踪的基本原理是:

  • 一次来自客户端的调用,对应一个唯一的traceId,用这个traceId来标识整个完整的调用链路

  • 用另外一个spanId来标识一次微服务的调用。通过spanId来标识微服务之间的层级关系。

    比如微服务A被调用,产生一个spanId为2,A又调用了另外2个微服务B和C,产生的spanId分别为2.1和2.2。这样就能清晰的知道微服务之间调用的层级关系

  • 对这些调用日志进行收集,进行简单的聚合操作后, 写入到NoSQL数据库中

  • 使用traceId就可以从NoSQL数据库中跟踪整个请求的链路,并得到各个链路的耗时

注意:由于一次客户端调用会导致很多次的微服务之间的调用,所以调用日志的量是巨大的。我们可以在收集调用日志后,进行一些简单的聚合操作,来减少日志量,或者通过采样的方式,只采样1%的调用日志。

配置管理

项目所使用的一些配置文件的管理。

将配置做成文件,这样是静态的配置。将配置放到一个服务上,这样可以实现动态的配置管理和更新。

配置主要分为两种:

  • 静态配置:比较固定的,不会经常发生变动的配置。比如数据库地址
  • 动态配置之:可能会经常发生变动的配置。比如超时时间。

静态配置通常放在项目的配置文件中即可,而动态的配置则通常放到配置服务中。

选型

  1. zookeeper,etcd,consul
  2. apollo(携程),diamond(淘宝),disconf(百度),都是以mysql为存储
  3. vintage(微博),以redis为存储
服务容错

一个系统拆成了多个服务。原本只有1个可能出现问题的点,现在变成了多个。

当系统出现未知问题,或者系统流量超出极限时,要如何应对?常见方式如下

  • 熔断:当下游服务不可用时,暂时屏蔽下游服务,以保障整体服务可用的方法
  • 降级:当流量激增时,对于某些页面或者服务,暂时屏蔽,以减轻服务器压力的方法
  • 限流:限制瞬时大流量以保护整体服务的方法

选型

  • hystrix
  • sentinel(阿里)
  • dubbo
负载均衡

为了应对大流量,通常对于一个服务会部署多台节点。那么此时就需要将流量分配到多个服务节点。

常见负载均衡算法

  • 一致性哈希
  • 随机
  • 轮询(加权轮询)
  • 最少连接(选择当前连接数最少的节点)
服务监控

从运维上对服务进行监控。

通常关注2个方面:需要监控什么?怎么监控?

服务监控的指标

  • 请求量:接口请求量,缓存请求量,数据库请求量
  • 响应时间:接口响应时间,缓存响应时间
  • 错误率:调用第三方服务错误率

监控系统构成

  • 数据采集:flume,filebeat
  • 数据处理:storm,spark
  • 数据存储:mysql,hbase,influxDB
  • 数据展示:grafana

RPC实例分析

以dubbo为例,进行分析

  • dubbo的微内核架构是如何设计的
  • dubbo是如何调用服务的
  • dubbo内嵌的服务治理策略是怎样的
dubbo介绍
  • dubbot是一款高性能的服务框架
  • 在2021年由阿里开源
  • 2018年,阿里将dubbo捐赠给Apache
  • 借鉴dubbo的RPC框架还有dubbox(当当),motan(微博)
dubbo架构特点
  • 微内核+扩展点架构
  • 微内核负责组装扩展点,实现整体逻辑
  • 扩展点可以让功能点被用户自定义实现

dubbo的扩展点是通过SPI(Service Provider Interface)机制实现的(借鉴了JDK的SPI机制)。dubbo会从以下目录加载实现类

  • META-INF/services/*
  • META-INF/dubbo/*
  • META-INF/dubbo/internal/*

格式:key=实现类的全限定名

在代码中即可通过这个key来加载某个接口的某个特定的实现类

dubbo的服务调用

你可能感兴趣的:(架构,rpc)