揭开gRPC神秘面纱

2023-11-05

一、什么是RPC

RPC(Remote Procedure Call):
远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。
RPC 是一种技术思想而非一种规范或协议,常见 RPC 技术和框架有:
应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud。
远程通信协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。
通信框架:MINA 和 Netty。
目前流行的开源 RPC 框架还是比较多的,有阿里巴巴的 Dubbo、Facebook 的 Thrift、Google 的 gRPC、Twitter 的 Finagle 等。


RPC 的核心功能是指实现一个 RPC 最重要的功能模块,就是上图中的”RPC 协议”部分:
一个 RPC 的核心功能主要有 5 个部分组成,分别是:

  • 客户端
  • 客户端 Stub
  • 网络传输模块
  • 服务端 Stub
  • 服务端

下面分别介绍核心 RPC 框架的重要组成:

  • 客户端(Client):服务调用方。
  • 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。
  • 服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。
  • 服务端(Server):服务的真正提供者。
  • Network Service:底层传输,可以是 TCP 或 HTTP。

百度百科上查找的资料
什么是代理和存根

打个比方,你到自动取款机上去取款;你就是客户,取款机就是你的代理;你不会在乎钱具体放在那里,你只想看到足够或更多的钱从出口出来(这就是com的透明性)。你同银行之间的操作完全是取款机代理实现。 你的取款请求通过取款机,传到另一头,银行的服务器,他也没有必要知道你在哪儿取钱,他所关心的是你的身份,和你取款多少。当他确认你的权限,就进行相应的操作,返回操作结果给取款机,取款机根据服务器返回结果,从保险柜里取出相应数量的钱给你。你取出卡后,操作完成。 取款机不是直接同服务器连接的,他们之间还有一个“存根”,取款机与存根通信,服务器与存根通信。从某种意义上说存根就是服务器的代理。

按照我自己通俗的理解,客户端与stub存根进行数据交互等价与直接与服务端通讯,对于接口调用相当于本地调用。

无非就是客户端将要调用服务端的接口和参数传递给client stub,client stub将上述信息序列化为能在网络上传输的数据,这一点应用完全没必要清楚细节。传输到server stub再将数据反序列化成实际调用接口的信息。server stub根据传输过来的信息调用服务端提供的服务接口,再将返回值通过stub传输给客户端。
简单归纳与普通的C/S通讯,仅仅多了一个中间件stub。

RPC 的核心功能主要由 5 个模块组成,如果想要自己实现一个 RPC,最简单的方式要实现三个技术点,分别是:

  • 服务寻址
  • 数据流的序列化和反序列化
  • 网络传输

在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。
这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。
只有二进制数据才能在网络中传输,序列化和反序列化的定义是:

  • 将对象转换成二进制流的过程叫做序列化
  • 将二进制流转换成对象的过程叫做反序列化

这个过程叫序列化和反序列化。同理,从服务端返回的值也需要序列化反序列化的过程。

通俗的理解:
现在我们都会在淘宝上买桌子,桌子这种很不规则不东西,该怎么从一个城市运输到另一个城市,这时候一般都会把它拆掉成板子,再装到箱子里面,就可以快递寄出去了,这个过程就类似我们的序列化的过程(把数据转化为可以存储或者传输的形式)。当买家收到货后,就需要自己把这些板子组装成桌子的样子,这个过程就像反序列 的过程(转化成当初的数据对象)。

详细内容参考该文章。
花了一个星期,我终于把RPC框架整明白了! - 51CTO.COM

二、什么是gRPC

在gRPC中,客户端应用程序可以直接在其他计算机上的服务器应用程序上调用方法,就好像它是本地对象一样,从而使您更轻松地创建分布式应用程序和服务。与许多RPC系统一样,gRPC围绕定义服​​务的思想,指定可通过其参数和返回类型远程调用的方法。在服务器端,服务器实现此接口并运行gRPC服务器以处理客户端调用。在客户端,客户端具有一个存根(在某些语言中仅称为客户端),提供与服务器相同的方法。
存根通俗理解为“开发票一式两份”,作为服务端方法的镜像或者说映射。简单说客户端执行存根的方法,等价于调用服务端的方法。


从Google内部的服务器到您自己的台式机,gRPC客户端和服务器可以在各种环境中运行并相互通信,并且可以使用gRPC支持的任何语言编写。因此,例如,您可以使用Go,Python或Ruby中的客户端轻松地用Java创建gRPC服务器。此外,最新的Google API的接口将具有gRPC版本,可让您轻松地在应用程序中构建Google功能。基于HTTP2.0的传输协议,高效多语言支持。

(简单的理解就是远程客户端可以直接调用服务端的接口。)

三、什么是Protobuf

1.概要

protobuf(Google Protocol Buffers)是Google提供一个具有高效的协议数据交换格式工具库。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
grpc作为rpc的一种,肯定有服务间的网络调用,调用就一定会有数据的传输,而这个数据的传输就用的是protobuf去序列化和反序列化的,一个请求的内容在调用方序列化通过网络传送到服务方,服务方就能用protobuf反序列化出来。
优势:

  • 平台无关,语言无关,可扩展;
  • 提供了友好的动态库,使用简单;
  • 解析速度快,比对应的XML快约20-100倍;
  • 序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10。


干货|了解Google远程过程调用(gRPC)技术

2.举例说明

helloworld.proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

说明:
(1)syntax = “proto3”
表示使用的protobuf编译器版本为v3

(2)option中的几个选项说明
选项不对message的定义产生任何的效果,只会在一些特定的场景中起到作用,下面是一部分例子,完整的选项列表可以前往google/protobuf/descriptor.proto查看
一些选项是文件级别的,意味着它可以作用于最外范围,不包含在任何消息内部、enum或服务定义中。一些选项是消息级别的,意味着它可以用在消息定义的内部。当然有些选项可以作用在域、enum类型、enum值、服务类型及服务方法中。到目前为止,并没有一种有效的选项能作用于所有的类型。

如下就是一些常用的选择:
java_multiple_files
option java_multiple_files = true; 该选项为true时,生成的Java类将是包级别的,否则会在一个包装类中。

java_package :
这个选项表明生成java类所在的包。如果在.proto文件中没有明确的声明java_package,就采用默认的包名。当然了,默认方式产生的 java包名并不是最好的方式,按照应用名称倒序方式进行排序的。如果不需要产生java代码,则该选项将不起任何作用。如:
option java_package = “com.example.foo”;

java_outer_classname ):
该选项表明想要生成Java类的名称。如果在.proto文件中没有明确的java_outer_classname定义,生成的class名称将会根据.proto文件的名称采用驼峰式的命名方式进行生成。如(foo_bar.proto生成的java类名为FooBar.java),如果不生成java代码,则该选项不起任何作用。如:
option java_outer_classname = “Ponycopter”;

objc_class_prefix:
设置Objective-C类的前缀,添加到所有Objective-C从此.proto文件产生的类和枚举类型。没有默认值,所使用的前缀应该是苹果推荐的3-5个大写字符,注意2个字节的前缀是苹果所保留的。
其他字段详细参考博客 。

(3)package helloworld
声明了一个包名,用来防止不同的消息类型命名冲突,类似于 namespace命名空间。

(4)定义service
定义一个SayHello的rpc方法,helloworld定义的是一个简单的RPC,客户端使用存根发送请求到服务器并等待响应返回,就像平常的函数调用一样。

rpc SayHello (HelloRequest) returns (HelloReply) {}

gRPC 允许你定义四类服务方法:
单项 RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。

rpc SayHello(HelloRequest) returns (HelloResponse){
}

服务端流式 RPC,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止。

rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}

客户端流式 RPC,即客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答。

rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}

双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持。

rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}

深入理解流,什么是流?

grpc将根据proto双方协商好的数据类型,序列化为二进制进行传输。之后再反序列化恢复数据原样。

(5)定义消息类型
指定字段类型
HelloRequest 消息包含一个string类型。
分配标示号
在消息定义中,每个字段都有唯一的一个数字标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。

注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。

切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。

最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]( (从FieldDescriptor::kFirstReservedNumber 到 FieldDescriptor::kLastReservedNumber))的标识号, Protobuf协议实现中对这些进行了预留。如果非要在.proto文件中使用这些预留标识号,编译时就会报警。同样你也不能使用早期保留的标识号。

默认值
当一个消息被解析的时候,如果被编码的信息不包含一个特定的singular元素,被解析的对象锁对应的域被设置位一个默认值,对于不同类型指定如下:
对于strings,默认是一个空string
对于bytes,默认是一个空的bytes
对于bools,默认是false
对于数值类型,默认是0
对于枚举,默认是第一个定义的枚举值,必须为0;

Protobuf3语法详解 - 望星辰大海 - 博客园

3.protoc是什么?

protoc是proto文件的编译器,目前可以将proto文件编译成C++、Java、Python三种代码文件,编译格式如下:

protoc -I=$SRC_DIR --cpp_out=$DST_DIR /path/to/file.proto

上面的命令会生成xxx.pb.h 和 xxx.pb.cc两个C++文件。

个人测试
测试命令

protoc -I=/home/workspace/test/grpc_test --cpp_out=/home/workspace/test/grpc_test/out /home/workspace/test/grpc_test/helloworld.proto


proto文件的作用?
定义我们程序中需要处理的结构化数据。message为结构化数据。简单理解为结构体,里面记录要存储的数据类型。

编译 .proto 文件的作用是什么?
写好 proto 文件之后就可以用 Protobuf 编译器将该文件编译成目标语言了。
helloworld.pb.h , 定义了 C++ 类的头文件
helloworld.pb.cc , C++ 类的实现文件
在生成的头文件中,定义了一个 C++ 类 helloworld,

四、参考资料

序列化和反序列化理解 - 简

花了一个星期,我终于把RPC框架整明白了! - 51CTO.COM

gRPC –指南

gRPC 官方文档中文版_V1.0

Protobuf学习 - 入门 - Aut - 博客园

干货|了解Google远程过程调用(gRPC)技术,这一篇就够了

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

揭开gRPC神秘面纱 的相关文章

  • Docker compose找不到本地包

    因此 我在 GOlang 中创建了 REST api 和一个 grpc 服务 现在我想在 docker compose 中组合它 我的 docker compose 看起来像这样 version 3 services db image po
  • 为什么 ManagedChannelBuilder 没有用于与服务器建立 TLS 连接的 TLS 参数?

    在这个例子中https github com grpc grpc java blob master interop testing src test java io grpc testing integration TlsTest java
  • Grpc Client 抛出 Grpc.Core.RpcException (响应协议降级为 HTTP/1.1。)

    我度过了最后三天 阅读焦点 尝试不同版本的 VS 和 NET 检查 Stackoverflow 中的问题 github 中的问题 关于我的 Grpc 客户端 服务器启动正常但客户端无法工作 我尝试过Grpcurl 工作正常 but C 客户
  • C# gRPC 客户端拦截器设置授权标头

    我正在尝试为 gRPC 客户端创建一个拦截器 始终设置配置的 API 令牌 问题是我找不到设置的方法context Options Headers 如果我正在阅读文档 我需要致电WithHeaders方法 需要设置新的元数据 以便我可以添加
  • 检查 gRpc 服务器是否在 C# 中运行

    我正在用 C 编写一个 gRpc 服务器 如果服务器因任何原因关闭 我想添加自动恢复 重试实现 经过研究 我遇到了拦截器并感到兴奋 但看起来它只支持 Go 我找不到 gRpc C 的任何类似内容 如何在 gRpc CSharp 中处理自动恢
  • 启动 RPC 服务器时出现“端点重复”

    我的程序使用 Microsoft RPC 进行进程间通信 为了准备接收 RPC 调用 程序运行以下序列 RpcServerUseProtseqEp 然后 RpcServerRegisterIf 则 RpcServerListen 该程序按照
  • PHP 的同步 AMQP

    PHP 能否像 RPC 服务一样对待 AMQP 发送消息并阻塞直到返回回复 是否有任何好的示例 是否有任何库以易于使用的方式包装此类功能 我希望拥有代理消息传递系统的灵活性 但避免 Web 层需要了解其异步性质 当然 绝对 看看 RPC 风
  • Hedera 上几乎相同的交易中“gasUsed”值存在巨大差异 - 为什么?

    我注意到所使用的气体量之间存在差异 通过交易几乎是相同的 我正在调用智能合约 连续两次使用相同的参数 两者之间的唯一区别 是我正在设置gasLimit到精确值 由返回eth estimateGas在第一个中 我正在设置gasLimit to
  • 我可以为 Istio 预置的经典 AWS ELB 定义子域吗?

    我将 Istio 部署在 AWS EKS 托管的 Kubernetes 集群中 这创建了一个名为 istio ingressgateway 的 LoadBalancer 类型的 Kubernetes 服务 其外部主机名为 redacted
  • gRPC / Protobuf 接口版本控制

    假设我们使用 gRCP Protobuf 来连接许多应用程序 这些应用程序是由他们自己的团队以自己的速度开发和发布的 随着时间的推移 同一应用程序将出现不同版本 例如 安装在用户 PC 上的桌面应用程序 它们在定义的界面上使用不同的版本 虽
  • 后台线程c++中的grpc服务器

    我正在尝试在 MFC 应用程序的线程中运行 grpc 服务器 我有 直接来自 GRPC 示例的 grpc 部分 MyAppDlg h include
  • GRPC:客户端超时

    我正在尝试让客户端在超时的情况下工作 为此 我修改了 async greeter server cpp 和 async greeter client cpp 文件来测试这个概念 我在客户端 在客户端上下文上 设置截止日期 如果超时 我会等待
  • 远程过程调用认证

    我正在使用远程过程调用 RPC 在本地计算机上通信数据 我的要求是使用 RPC 在两个处理之间通信数据 但服务器应该通过某种方式对客户端进行身份验证 我遇到了 RpcBindingSetAuthInfo 它设置身份验证和授权信息 第四个参数
  • GWT:在另一个模块内调用RPC服务

    我有一个模块B 它继承了模块A 当我从A内部调用RPC服务时 它们工作正常 但是当我在B中调用A的服务时 RPC调用总是失败 我错过了什么吗 预先感谢您的任何帮助 我在这里找到了我的问题的答案 http blog cloudglow com
  • 导入“google/api/annotations.proto”未找到或有错误。如何将其添加为依赖项?

    按照文档如何设置 gRPC 网关 https github com grpc ecosystem grpc gateway 我发现自己陷入了生成 grpc 网关的第四步 也就是说 当添加以下行时 事情就会崩溃 import google a
  • java.lang.ClassNotFoundException:com.google.gwt.user.client.rpc.RemoteService

    在 Tomcat 6 中部署 war 文件时出现以下异常 java lang ClassNotFoundException com google gwt user client rpc RemoteService 所以我尝试通过 webAp
  • 在 grpc python 中处理异步流请求

    我试图了解如何使用双向流处理 grpc api 使用 Python API 假设我有以下简单的服务器定义 syntax proto3 package simple service TestService rpc Translate stre
  • 检查 grpc 服务器可用性?

    有什么方法可以检查吗grpc server无需进行实际的过程调用和实现额外的查询 即rpc HealthCheck Input returns Status 大多数客户应该使用通道状态API https github com grpc gr
  • java.net.SocketException:无效参数:与 BungeeCord 连接

    我编写了一个使用 gRPC 连接到服务器的 Java 依赖项 在我的 spigot 插件和普通 java 项目中使用此依赖关系工作正常 但在 BungeeCord 插件中使用它会产生以下异常 Caused by io grpc netty
  • Grpc - 将消息从一个客户端发送到连接到同一服务器的另一个客户端

    是否可以将消息从一个客户端发送到连接到同一服务器的另一个客户端 我想将数据从一个客户端发送到服务器然后发送到特定客户端 我想我需要获取客户端 ID 但我不知道如何获取此 ID 以及如何从服务器将此消息发送到该客户端 我这里有一个样本 这是一

随机推荐

  • Java读写Excel

    一 Excel基本概念 Workbook 工作簿 代表一个Excel文件 Excel分为两种 后缀名为xls的HSSFWorkBook 2003版本及以前 和后缀名为xlsx的XSSFWorkBook 2007版本及以后 Sheet 表格
  • 解决vscode中不能使用yarn命令

    由于vscode中的集成终端使用的是powershell 所以我们要设置一下powershell的执行权限 解决方法 进入C Windows System32 WindowsPowerShell v1 0目录 找到powershell ex
  • Python使用defaultdict解决字典默认值的问题

    文章目录 1 导入defaultdict 2 创建defaultdict 3 使用defaultdict 4 添加defaultdict默认值 5 结论 在Python中 defaultdict是一种特殊类型的字典 它可以自动为字典中不存在
  • C++学习——拷贝构造,拷贝赋值,静态变量,单例对象,成员指针

    十七 拷贝构造和拷贝赋值 1 浅拷贝和深拷贝 1 如果类中包含指针形式的成员变量 缺省的拷贝构造函数只是复制了指针本身 而没有复制所指向的内容 这种拷贝方式称为浅拷贝 2 浅拷贝会导致不同的对象之间的数据共享 如果数据在堆区 析构函数还会引
  • K8s系列---【资源不足:0/3 nodes are available: 1 Insufficient cpu, 2 node(s) had taint {node-role.kubernetes...

    1 背景 我用KubeSphere创建了一个工作负载 在增加副本数量时 报了下面的错 2 报错 0 3 nodes are available 1 Insufficient cpu 2 node s had taint node role
  • python中的insert函数_Python 列表 insert() 使用方法及示例

    Python 列表 insert 使用方法及示例 insert 方法将元素添加到列表中的指定索引处 insert 方法的语法是list insert index element insert 参数 insert 函数采用两个参数 index
  • The connection to the server localhost:8080 was refused - did you specify the right host or port?解决

    问题分析 环境变量 原因 kubernetes master没有与本机绑定 集群初始化的时候没有绑定 此时设置在本机的环境变量即可解决问题 问题图片 解决方式 步骤一 设置环境变量 具体根据情况 此处记录linux设置该环境变量 方式一 编
  • ubuntu中自带的ufw防火墙

    ufw是一个基于主机的 host based iptable防火墙 我使用的操作系统是Ubuntu10 04 ufw防火墙是默认安装的 下面是我用到的一些ufw命令 根据man文档整理 1 查看防火墙的状态 sudo ufw status
  • 2014年3月3日星期一(DEMO8-3,ALPHA混合)

    单位的电脑运行DX11时 好多问题 但是没法上网 因此 以后在家进行DX11 在单位进行软引擎 因为一是以后会有大例子 需要较长时间 二是从书上看的东西 问题的解决方法不用在网上找 大不了拷到优盘里 言归正传 进行8 3 实际上是ALPHA
  • Qt中的窗口类及其特点

    目录 常用的窗口类 窗口的显示内嵌窗口 QWidget内嵌窗口演示 QWidget不内嵌窗口演示 QDialog类型的窗口特点 QMainWindows窗口的特点 总结 常用的窗口类 常用的窗口类有 3 个 在创建 Qt 窗口的时候 需要让
  • 不出意外的话,2023年是AI大模型元年

    这两天听的最多的新闻莫过于 谁谁谁 AI 大模型 面向全社会开放使用 文心一言 WPSAI 讯飞星火 百川智能等等 2023年 AI大模型注定在历史上增添了浓妆淡抹的一幕 未来 AI 将与各个软件应用如影随形 如云计算一般成为基础能力 按照
  • dvwa靶场的简单练习

    此文章仅为记录自己打靶场的过程 一 dvwa靶场的搭建 1 phpstudy的下载安装以及配置 下载phpstudy 官网链接 小皮面板 phpstudy 让天下没有难配的服务器环境 xp cn 下载好后直接安装 选盘看个人喜好 安装后如图
  • spring 01 :基础(IoC控制反转、DI依赖注入)、整合Junit、整合web

    struts web层 比较简单 ValueStack值栈 拦截器 hibernate dao层 知识点杂 spring service层 重要 会多少用多少 gt 了解 spring day01 基础 IoC控制反转 DI依赖注入 整合J
  • 《数据安全法》今日实施,中国信通院联合百度等企业发起“数据安全推进计划”

    9月1日讯 数据安全法 今天正式实施 个人信息保护法 也将于11月1日实施 这标志着我国数据安全制度建设进入了新的阶段 发展数字经济 加快培育数据要素市场 必须把保障数据安全放在突出位置 为推动法律法规及监管要求的贯彻落实 促进数据安全技术
  • Raid0、Raid1、Raid5及Raid10的区别

    博主推荐谷歌搜索 看Youtube视频 国外学习网站 Github必备神器 SockBoom 或者网址 SockBoomhttps sockboom shop auth register affid 212828 一 概况 Raid Red
  • Python中 ''.JOIN()的用法

    Python join 方法 描述 将序列中的元素以指定的字符连接生成一个新的字符串 语法 语法 sep join seq 参数说明 sep 分隔符 可以为空 seq 要连接的元素序列 字符串 元组 字典 返回值 返回通过指定字符连接序列中
  • 机器自主学习创造新数据

    近年来 人工智能 AI 的发展带来了许多革命性的改变 其中 生成式AI Generative AI 也被称为AIGC Artificial IntelligenceGenerated Content 引起了人们的极大关注 生成式AI是一种使
  • 内存回收

    内存回收 内存状态分为 使用 未使用 可回收 这几点有啥区别 使用 标记状态已使用 未使用 使用地址到结束地址 可回收 标记状态为回收 怎么判断内存可回收 因为调用释放接口了 怎么回收性能高 批量回收 不要回收太频繁 避免磁盘碎片 磁盘内碎
  • QT基础:QPainte 绘制文本并设置动态设置字体演示

    QPainte 是QT里面的一个绘制控件 这里演示的是 用 QPainte 绘制一个文本 并通过 ui 上的 fontComboBox 控件 改变文本字体后触发 widget 槽函数 update 来刷新界面 演示过于简单 适合初学者食用
  • 揭开gRPC神秘面纱

    一 什么是RPC RPC Remote Procedure Call 远程过程调用 它是一种通过网络从远程计算机程序上请求服务 而不需要了解底层网络技术的思想 RPC 是一种技术思想而非一种规范或协议 常见 RPC 技术和框架有 应用级的服