RPC技术概述及官方示例实践

12/5/2022 RPCgRPCDubbo远程过程调用

# 1. RPC技术概述

# 1.1 什么是RPC

RPC(Remote Procedure Call Protocol)远程过程调用协议。一个通俗的描述是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。比较正式的描述是:一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。

那么我们至少从这样的描述中挖掘出几个要点:

  • RPC是协议:既然是协议就只是一套规范,那么就需要有人遵循这套规范来进行实现。目前典型的RPC实现包括:Dubbo、Thrift、gRPC等。
  • 网络协议和网络IO模型对其透明:既然RPC的客户端认为自己是在调用本地对象。那么传输层使用的是TCP/UDP还是HTTP协议,又或者是一些其他的网络协议它就不需要关心了。
  • 信息格式对其透明:我们知道在本地应用程序中,对于某个对象的调用需要传递一些参数,并且会返回一个调用结果。至于被调用的对象内部是如何使用这些参数,并计算出处理结果的,调用方是不需要关心的。那么对于远程调用来说,这些参数会以某种信息格式传递给网络上的另外一台计算机,这个信息格式是怎样构成的,调用方是不需要关心的。
  • 应该有跨语言能力:为什么这样说呢?因为调用方实际上也不清楚远程服务器的应用程序是使用什么语言运行的。那么对于调用方来说,无论服务器方使用的是什么语言,本次调用都应该成功,并且返回值也应该按照调用方程序语言所能理解的形式进行描述。
RPC过程调用模型图

# 1.2 为什么要用RPC

其实这是应用开发到一定的阶段的强烈需求驱动的。如果我们开发简单的单一应用,逻辑简单、用户不多、流量不大,那我们用不着。当我们的系统访问量增大、业务增多时,我们会发现单机系统已经无法承受。此时我们可以将业务拆分成几个互不关联的应用,分别部署在各自机器上,以划清逻辑并减小压力。此时,我们也可以不需要RPC,因为应用之间是互不关联的。

当我们的业务越来越多、应用也越来越多时,自然的,我们会发现有些功能已经不能简单划分开来或者划分不出来。此时可以将公共业务逻辑抽离出来,将之组成独立的服务Service应用 。而原有的、新增的应用都可以与那些独立的Service应用交互,以此来完成完整的业务功能。所以此时,我们急需一种高效的应用程序之间的通讯手段来完成这种需求,这时候就需要RPC来大显身手了。

# 1.3 RPC的基本原理

# 1.3.1 RPC基本组成

在一个典型 RPC 的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件。

完整RPC框架的组成

一个 RPC 的核心功能主要有 5 个部分组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等。

  • 客户端(Client):服务调用方。

  • 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。

  • 服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。

  • 服务端(Server):服务的真正提供者。

  • 网络传输服务(Network Service):底层传输,可以是 TCP 或 HTTP。

    RPC网络传输的流程

# 1.3.2 RPC调用流程

一次RPC调用流程包含以下步骤。

  • Step1:服务消费者(Client 客户端)通过本地调用的方式调用服务。
  • Step2:客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体。
  • Step3:客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端。
  • Step4:服务端存根(Server Stub)收到消息后进行解码(反序列化操作)。
  • Step5:服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理
  • Step6:服务端(Server)本地服务业务处理。
  • Step7:处理结果返回给服务端存根(Server Stub)。
  • Step8:服务端存根(Server Stub)序列化结果。
  • Step9:服务端存根(Server Stub)将结果通过网络发送至消费方。
  • Step10:客户端存根(Client Stub)接收到消息,并进行解码(反序列化)。
  • Step11:服务消费方得到最终结果。

RPC调用流程

# 2. RPC技术选型

RPC 协议只规定了 Client 与 Server 之间的点对点调用流程,包括 stub、通信协议、RPC 消息解析等部分,在实际应用中,还需要考虑服务的高可用、负载均衡等问题,所以这里的 RPC 框架指的是能够完成 RPC 调用的解决方案,除了点对点的 RPC 协议的具体实现外,还可以包括服务的发现与注销、提供服务的多台 Server 的负载均衡、服务的高可用等更多的功能。 目前的 RPC 框架大致有两种不同的侧重方向,一种偏重于服务治理,另一种偏重于跨语言调用 。

偏重于服务治理——服务治理型

  • 服务治理型的RPC框架有Dubbo、DubboX等,Dubbo是阿里开源的分布式服务框架,能够实现高性能RPC调用,并且提供了丰富的管理功能,是十分优秀的RPC框架。DubboX是基于Dubbo框架开发的RPC框架,支持REST风格远程调用,并增加了一些新的feature。
  • 这类RPC框架的特点是功能丰富,提供高性能的远程调用以及服务发现及治理功能,适用于大型服务的微服务化拆分以及管理,对于特定语言如java的项目可以十分友好的透明化接入,但缺点是语言耦合度较高,跨语言支持难度较大。

偏重于跨语言调用——跨语言调用型

  • 跨语言调用型的RPC框架有Thrift、gRPC、Hessian、Hprose等,这一类的RPC框架重点关注于服务的跨语言调用,能够支持大部分的语言进行语言无关的调用,非常适合于为不同语言提供通用远程服务的场景。但这类框架没有服务发现相关机制,实际使用时一般需要代理层进行请求转发和负载均衡策略控制。

# 2.1 gRPC

# 2.1.1 基本介绍

gRPC是一个高性能、开源、通用的RPC框架,由Google推出,基于HTTP2协议标准设计开发,默认采用Protocol Buffers数据序列化协议,支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。

在gRPC客户端可以直接调用不同服务器上的远程程序,使用姿势看起来就像调用本地程序一样,很容易去构建分布式应用和服务。和很多RPC系统一样,服务端负责实现定义好的接口并处理客户端的请求,客户端根据接口描述直接调用需要的服务。客户端和服务端可以分别使用gRPC支持的不同语言实现。

gRPC基本介绍

官方网站:https://grpc.io/ (opens new window)(具体要在哪个语言里使用gRPC就去官网找对应语言的教程即可)

gRPC官网

# 2.1.2 主要特性

[1] 强大的IDL

  • gRPC使用ProtoBuf来定义服务,ProtoBuf是由Google开发的一种数据序列化协议(类似于XML、JSON、hessian)。ProtoBuf能够将数据进行序列化,并广泛应用在数据存储、通信协议等方面。

[2] 多语言支持

  • gRPC支持多种语言,并能够基于语言自动生成客户端和服务端功能库。目前已提供了C版本grpc、Java版本grpc-java 和 Go版本grpc-go,其它语言的版本正在积极开发中,其中,grpc支持C、C++、Node.js、Python、Ruby、Objective-C、PHP和C#等语言,grpc-java已经支持Android开发。

[3] HTTP2

  • gRPC基于HTTP2标准设计,所以相对于其他RPC框架,gRPC带来了更多强大功能,如双向流、头部压缩、多复用请求等。这些功能给移动设备带来重大益处,如节省带宽、降低TCP链接次数、节省CPU使用和延长电池寿命等。同时,gRPC还能够提高了云端服务和Web应用的性能。gRPC既能够在客户端应用,也能够在服务器端应用,从而以透明的方式实现客户端和服务器端的通信和简化通信系统的构建。

# 2.2 Dubbo

# 2.2.1 基本介绍

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

Dubbo3 定义为面向云原生的下一代 RPC 服务框架。3.0 基于 Dubbo 2.x 演进而来,在保持原有核心功能特性的同时, Dubbo3 在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

Dubbo架构图

官方网站:https://dubbo.apache.org/zh/ (opens new window)(目前主要支持Java、Go等多种语言)

Dubbo官网

# 2.2.2 主要特性

[1] 高性能 RPC 通信协议

  • 跨进程或主机的服务通信是 Dubbo 的一项基本能力,Dubbo RPC 以预先定义好的协议编码方式将请求数据(Request)发送给后端服务,并接收服务端返回的计算结果(Response)。RPC 通信对用户来说是完全透明的,使用者无需关心请求是如何发出去的、发到了哪里,每次调用只需要拿到正确的调用结果。

[2] 自动服务(地址)发现

  • Dubbo 的服务发现机制,让微服务组件之间可以独立演进并任意部署,消费端可以在无需感知对端部署位置与 IP 地址的情况下完成通信。Dubbo 提供的是 Client-Based 的服务发现机制,使用者可以有多种方式启用服务发现。

[3] 运行态流量管控

  • 透明地址发现让 Dubbo 请求可以被发送到任意 IP 实例上,这个过程中流量被随机分配。当需要对流量进行更丰富、更细粒度的管控时,就可以用到 Dubbo 的流量管控策略,Dubbo 提供了包括负载均衡、流量路由、请求超时、流量降级、重试等策略,基于这些基础能力可以轻松的实现更多场景化的路由方案,Dubbo 支持流控策略在运行态动态生效,无需重新部署。

[4] 丰富的扩展组件及生态

  • Dubbo 强大的服务治理能力不仅体现在核心框架上,还包括其优秀的扩展能力以及周边配套设施的支持。通过 Filter、Router、Protocol 等几乎存在于每一个关键流程上的扩展点定义,可以丰富 Dubbo 的功能或实现与其他微服务配套系统的对接,包括 Transaction、Tracing 目前都有通过 SPI 扩展的实现方案。

[5] 面向云原生设计

  • Dubbo 从设计上是完全遵循云原生微服务开发理念的,这体现在多个方面,首先是对云原生基础设施与部署架构的支持,包括 容器、Kubernetes 等;另一方面,Dubbo 众多核心组件都已面向云原生升级,包括 Triple 协议、统一路由规则、对多语言的支持。

# 3. gRPC官方示例实践

gRPC官方提供了使用示例,也许是依赖环境不同,照着官方文档操作会踩一些坑。本文的测试环境为 MacOS13 + Python3.9 + Java8 + Maven3.8.6。

# 3.1 gRPC-Python

gRPC-Python示例的官方文档:https://grpc.io/docs/languages/python/quickstart/ (opens new window)

# 3.1.1 下载示例源码并安装依赖

项目地址:https://github.com/grpc/grpc (opens new window)(示例代码在 grpc/examples/python/helloworld 目录下)

运行示例项目需要安装 grpcio 和 protobuf 库。

$ pip3 install grpcio
$ pip3 install protobuf==4.21
1
2

注:protobuf库需要指定版本,否则运行示例项目时会报如下错误ImportError: cannot import name 'builder' from 'google.protobuf.internal'

# 3.1.2 测试gRPC-Python示例程序

运行 greeter_server.py 和 greeter_client.py 即可,输出如下内容即为启动成功。

$ python3 greeter_server.py
Server started, listening on 50051

$ python3 greeter_client.py
Will try to greet world ...
Greeter client received: Hello, you!
1
2
3
4
5
6

# 3.2 gRPC-Java

# 3.2.1 下载示例源码并安装依赖

项目地址:https://github.com/grpc/grpc-java (opens new window)(示例代码在 grpc-java/examples 目录下)

$ git clone https://github.com/grpc/grpc-java
$ cd grpc-java/examples
1
2

配置好Maven,并拉取相关依赖,然后执行 mvn compile命令编译一下,这时虽然IDEA里依然显示爆红,但已经可以运行了。

# 3.2.2 测试gRPC-Java示例程序

先启动 HelloWorldServer.java

十二月 05, 2022 7:28:20 下午 io.grpc.examples.helloworld.HelloWorldServer start
信息: Server started, listening on 50051
1
2

后启动 HelloWorldClient.java

十二月 05, 2022 7:28:37 下午 io.grpc.examples.helloworld.HelloWorldClient greet
信息: Will try to greet world ...
十二月 05, 2022 7:28:38 下午 io.grpc.examples.helloworld.HelloWorldClient greet
信息: Greeting: Hello world
1
2
3
4

输出以上内容即为示例程序启动成功。

注意事项:跑gRPC-Java示例程序时,HelloWorldClient遇到了以下报错。我把50051端口的进程kill掉,重启就好了。

RPC failed: Status{code=UNKNOWN, description=null, cause=null}
1

# 4. Dubbo官方示例实践

Dubbo-Java示例的官方文档:https://cn.dubbo.apache.org/zh/docs3-v2/java-sdk/quick-start/spring-boot/ (opens new window)

# 4.1 搭建ZooKeeper服务

Dubbo 的运行需要依赖 ZooKeeper 服务,如果没有的话,可以使用 Docker Compose 搭建一个。

docker-compose.yml

version: '3.2'
services:
  zookeeper:
    image: wurstmeister/zookeeper
    container_name: zookeeper
    ports:
      - "2181:2181"
    restart: always
1
2
3
4
5
6
7
8

执行如下命令运行容器。

$ docker-compose up -d     // 运行容器
1

# 4.2 测试Dubbo-Java示例程序

# 4.2.1 下载示例源码并修改配置

$ git clone https://github.com/apache/dubbo-samples.git
$ cd dubbo-samples/1-basic/dubbo-samples-spring-boot
$ tree -L 2

.
├── README.md
├── dubbo-samples-spring-boot-consumer
│   ├── pom.xml
│   └── src
├── dubbo-samples-spring-boot-interface
│   ├── pom.xml
│   └── src
├── dubbo-samples-spring-boot-provider
│   ├── pom.xml
│   └── src
└── pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

配置好Maven,并拉取相关依赖。修改 application.yml 里的 Zookeeper 服务地址,默认是 zookeeper://127.0.0.1:2181

# 4.2.2 运行Dubbo-Java示例程序

先启动 ProviderApplication.java

Hello World, request from consumer: xxx.xxx.xxx.xxx
1

后启动 ConsumerApplication.java

result: Hello World
1

输出以上内容即为示例程序启动成功。

# 5. 参考资料

[1] RPC核心原理是什么?以及常用技术有哪些?from CSDN (opens new window)

[2] RPC框架:从原理到选型,一文带你搞懂RPC from 51CTO (opens new window)

[3] 如何理解 RPC 远程服务调用 from 技术文章摘抄 (opens new window)

[4] 架构设计:如何实现一个高性能分布式 RPC 框架 from 技术文章摘抄 (opens new window)

[5] RPC介绍与原理 from CSDN (opens new window)

[6] RPC框架(一)RPC简介 from CSDN (opens new window)

[7] RPC简介以及与RESTful对比 from Helloted Blog (opens new window)

[8] gRPC简介 from Gitbook (opens new window)

[9] Dubbo简介 from Dubbo官网 (opens new window)

[10] rpc框架之 grpc vs dubbo 性能比拼 from CSDN (opens new window)

[11] 示例演示了如何使用 Spring Boot 方式快速开发 Dubbo 应用 from Dubbo官网 (opens new window)

[12] ImportError: cannot import name 'builder' from 'google.protobuf.internal' from stackoverflow (opens new window)

Last Updated: 1/8/2023, 1:44:28 PM