rpc简介及原理

article/2025/9/26 19:39:29

1.RPC简介及原理介绍

RPC技术内部原理是通过两种技术的组合来实现的:本地方法调用 和 网络通信技术。

1.1 RPC简介

在上述本地过程调用的例子中,我们是在一台计算机上执行了计算机上的程序,完成调用。随着计算机技术的发展和需求场景的变化,有时就需要从一台计算机上执行另外一台计算机上的程序的需求,因此后来又发展出来了RPC技术。特别是目前随着互联网技术的快速迭代和发展,用户和需求几乎都是以指数式的方式在高速增长,这个时候绝大多数情况下程序都是部署在多台机器上,就需要在调用其他物理机器上的程序的情况。

RPC是Remote Procedure Call Protocol三个单词首字母的缩写,简称为:RPC,翻译成中文叫远程过程调用协议。所谓远程过程调用,通俗的理解就是可以在本地程序中调用运行在另外一台服务器上的程序的功能方法。这种调用的过程跨越了物理服务器的限制,是在网络中完成的,在调用远端服务器上程序的过程中,本地程序等待返回调用结果,直到远端程序执行完毕,将结果进行返回到本地,最终完成一次完整的调用。

需要强调的是:远程过程调用指的是调用远端服务器上的程序的方法整个过程。

1.2 RPC设计组成

RPC技术在架构设计上有四部分组成,分别是:客户端、客户端存根、服务端、服务端存根。

这里提到了客户端服务端的概念,其属于程序设计架构的一种方式,在现代的计算机软件程序架构设计上,大方向上分为两种方向,分别是:B/S架构C/S架构。B/S架构指的是浏览器到服务器交互的架构方式,另外一种是在计算机上安装一个单独的应用,称之为客户端,与服务器交互的模式。

由于在服务的调用过程中,有一方是发起调用方,另一方是提供服务方。因此,我们把服务发起方称之为客户端,把服务提供方称之为服务端。以下是对RPC的四种角色的解释和说明:

  • **客户端(Client):**服务调用发起方,也称为服务消费者。
  • **客户端存根(Client Stub):**该程序运行在客户端所在的计算机机器上,主要用来存储要调用的服务器的地址,另外,该程序还负责将客户端请求远端服务器程序的数据信息打包成数据包,通过网络发送给服务端Stub程序;其次,还要接收服务端Stub程序发送的调用结果数据包,并解析返回给客户端。
  • **服务端(Server):**远端的计算机机器上运行的程序,其中有客户端要调用的方法。
  • **服务端存根(Server Stub):**接收客户Stub程序通过网络发送的请求消息数据包,并调用服务端中真正的程序功能方法,完成功能调用;其次,将服务端执行调用的结果进行数据处理打包发送给客户端Stub程序。

RPC原理及调用步骤

了解完了RPC技术的组成结构我们来看一下具体是如何实现客户端到服务端的调用的。实际上,如果我们想要在网络中的任意两台计算机上实现远程调用过程,要解决很多问题,比如:

  • 两台物理机器在网络中要建立稳定可靠的通信连接。
  • 两台服务器的通信协议的定义问题,即两台服务器上的程序如何识别对方的请求和返回结果。也就是说两台计算机必须都能够识别对方发来的信息,并且能够识别出其中的请求含义和返回含义,然后才能进行处理。这其实就是通信协议所要完成的工作。

让我们来看看RPC具体是如何解决这些问题的,RPC具体的调用步骤图如下:

img

在上述图中,通过1-10的步骤图解的形式,说明了RPC每一步的调用过程。具体描述为:

  • 1、客户端想要发起一个远程过程调用,首先通过调用本地客户端Stub程序的方式调用想要使用的功能方法名;
  • 2、客户端Stub程序接收到了客户端的功能调用请求,将客户端请求调用的方法名,携带的参数等信息做序列化操作,并打包成数据包。
  • 3、客户端Stub查找到远程服务器程序的IP地址,调用Socket通信协议,通过网络发送给服务端。
  • 4、服务端Stub程序接收到客户端发送的数据包信息,并通过约定好的协议将数据进行反序列化,得到请求的方法名和请求参数等信息。
  • 5、服务端Stub程序准备相关数据,调用本地Server对应的功能方法进行,并传入相应的参数,进行业务处理。
  • 6、服务端程序根据已有业务逻辑执行调用过程,待业务执行结束,将执行结果返回给服务端Stub程序。
  • 7、服务端Stub程序**将程序调用结果按照约定的协议进行序列化,**并通过网络发送回客户端Stub程序。
  • 8、客户端Stub程序接收到服务端Stub发送的返回数据,**对数据进行反序列化操作,**并将调用返回的数据传递给客户端请求发起者。
  • 9、客户端请求发起者得到调用结果,整个RPC调用过程结束。

RPC涉及到的相关技术

通过上文一系列的文字描述和讲解,我们已经了解了RPC的由来和RPC整个调用过程。我们可以看到RPC是一系列操作的集合,其中涉及到很多对数据的操作,以及网络通信。因此,对RPC中涉及到的技术做一个总结和分析:

  • 1、动态代理技术: 上文中我们提到的Client Stub和Sever Stub程序,在具体的编码和开发实践过程中,都是使用动态代理技术自动生成的一段程序。

  • 2、序列化和反序列化: 在RPC调用的过程中,我们可以看到数据需要在一台机器上传输到另外一台机器上。在互联网上,所有的数据都是以字节的形式进行传输的。而我们在编程的过程中,往往都是使用数据对象,因此想要在网络上将数据对象和相关变量进行传输,就需要对数据对象做序列化和反序列化的操作。

    • **序列化:**把对象转换为字节序列的过程称为对象的序列化,也就是编码的过程。
    • **反序列化:**把字节序列恢复为对象的过程称为对象的反序列化,也就是解码的过程。

我们常见的Json,XML等相关框架都可以对数据做序列化和反序列化编解码操作。同时,在之前的《Go语言微服务理论与实践》课程中,我们已经学习过Protobuf协议,这也是一种数据编解码的协议,在RPC框架中使用的更广泛。

2. grpc 流模式调用

一 服务端流 RPC

在服务端流模式的RPC实现中,服务端得到客户端请求后,处理结束返回一个数据应答流。在发送完所有的客户端请求的应答数据后,服务端的状态详情和可选的跟踪元数据发送给客户端。服务端流RPC实现案例如下:

1.1 服务接口定义

在.proto文件中定义服务接口,使用服务端流模式定义服务接口,如下所示:

...
//订单服务service定义
service OrderService {rpc GetOrderInfos (OrderRequest) returns (stream OrderInfo) {}; //服务端流模式
}

我们可以看到与之前简单模式下的数据作为服务接口的参数和返回值不同的是,此处服务接口的返回值使用了stream进行修饰。通过stream修饰的方式表示该接口调用时,服务端会以数据流的形式将数据返回给客户端。

1.2 编译.proto文件,生成pb.go文件

使用gRPC插件编译命令编译.proto文件,编译命令如下:

protoc --go_out=plugins=grpc:. message.proto

1.3 自动生成文件的变化

与数据结构体发送携带数据实现不同的时,流模式下的数据发送和接收使用新的功能方法完成。在自动生成的go代码程序当中,每一个流模式对应的服务接口,都会自动生成对应的单独的client和server程序,以及对应的结构体实现。具体编程如下图所示:

1.3.1 服务端自动生成

type OrderService_GetOrderInfosServer interface {Send(*OrderInfo) errorgrpc.ServerStream
}type orderServiceGetOrderInfosServer struct {grpc.ServerStream
}func (x *orderServiceGetOrderInfosServer) Send(m *OrderInfo) error {return x.ServerStream.SendMsg(m)
}

流模式下,服务接口的服务端提供Send方法,将数据以流的形式进行发送

1.3.2 客户端自动生成

type OrderService_GetOrderInfosClient interface {Recv() (*OrderInfo, error)grpc.ClientStream
}type orderServiceGetOrderInfosClient struct {grpc.ClientStream
}func (x *orderServiceGetOrderInfosClient) Recv() (*OrderInfo, error) {m := new(OrderInfo)if err := x.ClientStream.RecvMsg(m); err != nil {return nil, err}return m, nil
}

流模式下,服务接口的客户端提供Recv()方法接收服务端发送的流数据。

1.4 服务编码实现

定义好服务接口并编译生成代码文件后,即可根据规则对定义的服务进行编码实现。具体的服务编码实现如下所示:

//订单服务实现
type OrderServiceImpl struct {
}//获取订单信息s
func (os *OrderServiceImpl) GetOrderInfos(request *message.OrderRequest, stream message.OrderService_GetOrderInfosServer) error {fmt.Println(" 服务端流 RPC 模式")orderMap := map[string]message.OrderInfo{"201907300001": message.OrderInfo{OrderId: "201907300001", OrderName: "衣服", OrderStatus: "已付款"},"201907310001": message.OrderInfo{OrderId: "201907310001", OrderName: "零食", OrderStatus: "已付款"},"201907310002": message.OrderInfo{OrderId: "201907310002", OrderName: "食品", OrderStatus: "未付款"},}for id, info := range orderMap {if (time.Now().Unix() >= request.TimeStamp) {fmt.Println("订单序列号ID:", id)fmt.Println("订单详情:", info)//通过流模式发送给客户端stream.Send(&info)}}return nil
}

GetOrderInfos方法就是服务接口的具体实现,因为是流模式开发,服务端将数据以流的形式进行发送,因此,该方法的第二个参数类型为OrderService_GetOrderInfosServer,该参数类型是一个接口,其中包含Send方法,允许发送流数据。Send方法的具体实现在编译好的pb.go文件中,进一步调用grpc.SeverStream.SendMsg方法。

1.5 服务的注册和监听的处理

服务的监听与处理与前文所学内容没有区别,依然是相同的步骤:

func main() {server := grpc.NewServer()//注册message.RegisterOrderServiceServer(server, new(OrderServiceImpl))lis, err := net.Listen("tcp", ":8090")if err != nil {panic(err.Error())}server.Serve(lis)
}

1.6 客户端数据接收

服务端使用Send方法将数据以流的形式进行发送,客户端可以使用Recv()方法接收流数据,因为数据流失源源不断的,因此使用for无限循环实现数据流的读取,当读取到io.EOF时,表示流数据结束。客户端数据读取实现如下:

...
for {orderInfo, err := orderInfoClient.Recv()if err == io.EOF {fmt.Println("读取结束")return}if err != nil {panic(err.Error())}fmt.Println("读取到的信息:", orderInfo)}
...

1.7 运行结果

按照先后顺序,依次运行server.go文件和client.go文件,可以得到运行结果。

1.7.1 服务端运行结果

服务端流 RPC 模式
订单序列号ID: 201907300001
订单详情: {201907300001 衣服 已付款 {} [] 0}
订单序列号ID: 201907310001
订单详情: {201907310001 零食 已付款 {} [] 0}
订单序列号ID: 201907310002
订单详情: {201907310002 食品 未付款 {} [] 0}

1.7.2 客户端运行结果

客户端请求RPC调用:服务端流模式
读取到的信息: OrderId:"201907310001" OrderName:"\351\233\266\351\243\237" OrderStatus:"\345\267\262\344\273\230\346\254\276" 
读取到的信息: OrderId:"201907310002" OrderName:"\351\243\237\345\223\201" OrderStatus:"\346\234\252\344\273\230\346\254\276" 
读取到的信息: OrderId:"201907300001" OrderName:"\350\241\243\346\234\215" OrderStatus:"\345\267\262\344\273\230\346\254\276" 
读取结束

二 客户端流模式

上文演示的是服务端以数据流的形式返回数据的形式。对应的,也存在客户端以流的形式发送请求数据的形式。

2.1 服务接口的定义

与服务端同理,客户端流模式的RPC服务声明格式,就是使用stream修饰服务接口的接收参数,具体如下所示:

...
//订单服务service定义
service OrderService {rpc AddOrderList (stream OrderRequest) returns (OrderInfo) {}; //客户端流模式
}

2.2 编译.proto文件

使用编译命令编译.protow文件。客户端流模式中也会自动生成服务接口的接口。

2.2.1 自动生成的服务流接口实现

type OrderService_AddOrderListServer interface {SendAndClose(*OrderInfo) errorRecv() (*OrderRequest, error)grpc.ServerStream
}type orderServiceAddOrderListServer struct {grpc.ServerStream
}func (x *orderServiceAddOrderListServer) SendAndClose(m *OrderInfo) error {return x.ServerStream.SendMsg(m)
}func (x *orderServiceAddOrderListServer) Recv() (*OrderRequest, error) {m := new(OrderRequest)if err := x.ServerStream.RecvMsg(m); err != nil {return nil, err}return m, nil
}

SendAndClose和Recv方法是客户端流模式下的服务端对象所拥有的方法。

2.2.2 自动生成的客户端流接口实现

type OrderService_AddOrderListClient interface {Send(*OrderRequest) errorCloseAndRecv() (*OrderInfo, error)grpc.ClientStream
}type orderServiceAddOrderListClient struct {grpc.ClientStream
}func (x *orderServiceAddOrderListClient) Send(m *OrderRequest) error {return x.ClientStream.SendMsg(m)
}func (x *orderServiceAddOrderListClient) CloseAndRecv() (*OrderInfo, error) {if err := x.ClientStream.CloseSend(); err != nil {return nil, err}m := new(OrderInfo)if err := x.ClientStream.RecvMsg(m); err != nil {return nil, err}return m, nil
}

Send和CloseAndRecv是客户端流模式下的客户端对象所拥有的方法。

2.3 服务的实现

客户端流模式的服务接口具体实现如下:

//订单服务实现
type OrderServiceImpl struct {
}//添加订单信息服务实现
func (os *OrderServiceImpl) AddOrderList(stream message.OrderService_AddOrderListServer) error {fmt.Println(" 客户端流 RPC 模式")for {//从流中读取数据信息orderRequest, err := stream.Recv()if err == io.EOF {fmt.Println(" 读取数据结束 ")result := message.OrderInfo{OrderStatus: " 读取数据结束 "}return stream.SendAndClose(&result)}if err != nil {fmt.Println(err.Error())return err}//打印接收到的数据fmt.Println(orderRequest)}
}

2.4 服务的注册和监听处理

依然是采用相同的服务注册和监听处理方式对服务进行注册和监听处理。

func main() {server := grpc.NewServer()//注册message.RegisterOrderServiceServer(server, new(OrderServiceImpl))lis, err := net.Listen("tcp", ":8090")if err != nil {panic(err.Error())}server.Serve(lis)
}

2.5 客户端实现

客户端调用send方法流数据到服务端,具体实现如下:

...
//调用服务方法addOrderListClient, err := orderServiceClient.AddOrderList(context.Background())if err != nil {panic(err.Error())}//调用方法发送流数据for _, info := range orderMap {err = addOrderListClient.Send(&info)if err != nil {panic(err.Error())}}for {orderInfo, err := addOrderListClient.CloseAndRecv()if err == io.EOF {fmt.Println(" 读取数据结束了 ")return}if err != nil {fmt.Println(err.Error())}fmt.Println(orderInfo.GetOrderStatus())}

2.6 程序运行

2.6.1 服务端

运行案例,程序输出如下:

客户端流 RPC 模式
201907300001 衣服 已付款
201907310001 零食 已付款
201907310002 食品 未付款读取数据结束 客户端流 RPC 模式
201907300001 衣服 已付款
201907310001 零食 已付款
201907310002 食品 未付款读取数据结束

2.6.2 客户端

客户端运行程序输出如下:

客户端请求RPC调用:客户端流模式读取数据结束 读取数据结束了

三 双向流模式

上文已经讲过了服务端流模式和客户端流模式。如果将客户端和服务端两种流模式结合起来,就是第三种模式,双向流模式。即客户端发送数据的时候以流数据发送,服务端返回数据也以流的形式进行发送,因此称之为双向流模式。

3.1 双向流服务的定义

//订单服务service定义
service OrderService {rpc GetOrderInfos (stream OrderRequest) returns (stream OrderInfo) {}; //双向流模式
}

3.2 编译.proto文件

3.2.1 服务端接口实现

type OrderService_GetOrderInfosServer interface {Send(*OrderInfo) errorRecv() (*OrderRequest, error)grpc.ServerStream
}type orderServiceGetOrderInfosServer struct {grpc.ServerStream
}func (x *orderServiceGetOrderInfosServer) Send(m *OrderInfo) error {return x.ServerStream.SendMsg(m)
}func (x *orderServiceGetOrderInfosServer) Recv() (*OrderRequest, error) {m := new(OrderRequest)if err := x.ServerStream.RecvMsg(m); err != nil {return nil, err}return m, nil
}

3.2.2 客户端接口实现

type OrderService_GetOrderInfosClient interface {Send(*OrderRequest) errorRecv() (*OrderInfo, error)grpc.ClientStream
}type orderServiceGetOrderInfosClient struct {grpc.ClientStream
}func (x *orderServiceGetOrderInfosClient) Send(m *OrderRequest) error {return x.ClientStream.SendMsg(m)
}func (x *orderServiceGetOrderInfosClient) Recv() (*OrderInfo, error) {m := new(OrderInfo)if err := x.ClientStream.RecvMsg(m); err != nil {return nil, err}return m, nil
}


http://chatgpt.dhexx.cn/article/0d164iAL.shtml

相关文章

RPC原理解析

1.RPC原理解析 1.1 什么是RPC RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP/IP或UDP,为通信程序…

RPC实现原理

一、什么是RPC框架? RPC,全称为Remote Procedure Call,即远程过程调用,是一种计算机通信协议。 比如现在有两台机器:A机器和B机器,并且分别部署了应用A和应用B。假设此时位于A机器上的A应用想要调用位于B机…

什么是 RPC?RPC原理是什么?

什么是 RPC?RPC原理是什么? 什么是 RPC? RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务 A、B 部署在两台不同的机器…

【RK3399Pro学习笔记】三、Debian 9 安装 ROS (Thinker Edge R)

目录 配置源设置 Key安装初始化rosdep环境配置测试安装rosinstall卸载 平台:华硕 Thinker Edge R 瑞芯微 RK3399Pro 固件版本:Tinker_Edge_R-Debian-Stretch-V1.0.4-20200615 参考资料: RK3399(Debian9 - stretch) 安装 ROS Lunar —— WB8…

VMware安装Debian9及开发环境配置

序 需要用到一个搭载armv7架构A7内核的Linux设备,装的是Debian系统,为便于兼容该设备故用VMware安装一个Debian9虚拟机,整理总结了网上一些信息以供参考。 目录 一、安装Debian91、WMware中的配置2、安装镜像 二、开发环境配置1、设置快捷键打…

debian 升级linux内核,Debian 9更新到最新的Linux内核

喜欢折腾的同学,会豪气如何升级 Debian Stretch 的内核到新版。遗憾的是现在能搜到的升级 Debian Linux 内核的文章多数是使用Ubuntu的deb安装包,其实这样装上去是有问题,常见的问题是,总会提示需要 apt --fix-broken install 。 这里简单记录一下,如何使用Debian官方源更…

debian 9 ssh root权限登录

debian 9默认的一样不能用root用户登录,还是需要设置,方法如下: 一,打开终端,用普通用户登录 输入:su - 输入密码,切换到root用户 二,设置root用户登录 nano /etc/ssh/sshd_conf…

Debian9到Debian11的超详细升级教程

在云服务器商镜像页面中,可能无法选择一些系统最新的镜像版本。 就会造成一些软件不能顺利下载。 这时就需要自己手动下载并安装最新版的Debian系统了。 1、把当前版本的包更新到最新的版本 apt-get update apt-get upgrade apt-get dist-upgrade按q 直接回车 …

【linux实战】debian9安装python3.7

之前有写了一篇ubuntu18.04使用apt安装python各个版本的,但是最近上debian9上,发现那种apt安装方式不可行,因为ppa不支持debian系列。 Debian 9 默认安装有 Python 2.7 和 3.5。但是我们现在没法使用apt直接安装是没法安装自己想要的版本。 …

debian 系统启动服务器,Debian9添加开机自启动服务

升级到Debian9后,我们发现系统默认不带/etc/rc.local文件,但rc.local服务仍然保留 补齐缺失文件,让Debian9通过编辑/etc/rc.loacl文件为我们的服务添加开机自启动 1、添加rc-local.service #以下为一整条命令,一起复制运行cat >…

debian服务器系统安装,安装Debian 9(Stretch)最小服务器

在本指南中,我们将引导您使用netinstall CD ISO映像安装Debian 9(Stretch)Minimal Server 。 您将执行的此安装适用于构建未来可定制的服务器平台,而无需GUI(图形用户界面)。 您可以使用它来安装仅需要使用的必需软件包,我们将在将来的指南中向您展示。 但是,在进一步了解系…

debian9服务器系统,如何安装Debian 9(Stretch)最小服务器

本教程将介绍如何安装Debian 9(Stretch)最小服务器。 本指南的目的是提供一个最小的Debian设置,可以作为我们其他Debian 9教程的基础,以及在howtoing.com上完美的服务器指南 。 1要求 要安装Debian 9服务器系统,您将需要以下内容:…

debian9软件源的设置

文章目录 debian9软件源的设置概述笔记END debian9软件源的设置 概述 今天和同学讨论一个在debian9下自产软件的细节, 需要先编译这个软件工程. 他说在debian9或debian10下都可以编译. 我本地debian9虚拟机编译失败. 因为我这虚拟机环境是从.iso装的, 装到可以访问外网就做了…

IDEA连接数据库失败

问题描述 IDEA连接数据库输入用户名密码后点击测试连接,返回failed连接失败。 点击view查看失败原因 提示错误号为08001 分析原因 MySql 5.X版本和MySql8.X版本之间有个比较大的区别,MySql8.X在连接的时候需要设置时区,否则连接失败。 …

PHP连接数据库失败-解决方案

问题描述 今天在使用php代码连接数据库的时候发现死活都连接不上,在确认账号密码都没有错误的情况下,几乎找遍了答案都无法解决。 解决方法 通过不断的查阅资料及测试,发现问题在于本机地址localhost和127.0.0.1身上,当我把l…

用php连接数据库步骤,php连接数据库步骤详细介绍

本篇文章为大家简述一下PHP与数据库连接步骤,仅供参考。 1:链接数据库 ①主机地址 ②mysql用户名 ③mysql密码 ④选择连接的数据库 ⑤端口号mysqli_connect($localhost, $user, $password, $port); 返回:如果连接成功,返回资源类型…

CDH 5.13 hue数据库连接测试失败

在自己的虚拟机部署了好几次hue连接测试不通过,暂时用不到hue,就一直没有管。 路径; /opt/cm-5.13.1/log/cloudera-scm-server 执行 tail -n 500 cloudera-scm-server.log 报错信息: 解决办法:安装mysql的时候,将…

用友系统中连接数据库服务器失败,用友u8服务器连接不到本地数据库

用友u8服务器连接不到本地数据库 内容精选 换一换 创建外表语法(CREATE FOREIGN TABLE (SQL on Hadoop or OBS))中,需指定一个与MRS数据源连接相关联的外部服务器。当您通过GaussDB(DWS)管理控制台创建MRS数据源连接时,数据库管理员dbadmin会在默认数据库gaussdb中自动创建一…

达梦数据库连接失败解决方式

打开达梦数据库之后,连接失败,检查主机名、用户名、口令后依旧出现下图错误 点开详情后,提示错误消息:网络通信异常 排查发现,本地的达梦服务没有启动 解决办法: 首先,打开任务管理器里面的服…

关于Java连接mysql数据库失败的一种检查思路

今天学习《Java从入门到精通》李晓波版本时,用Java连接数据库失败,报了下面错误 这里贴个连接数据库的代码 package del; import java.sql.*; public class Java16_1 {public static void main(String[] args) {// TODO Auto-generated method stubConn…