用一个简单的函数来理一下RPC调用过程

article/2025/10/4 2:43:09

1.什么是RPC

RPC(Remote Procedure Call)远程过程调度,简单的理解就是一个节点请求另一个节点的提供的服务。

2.远程调用要面临的三个问题。

(1)Call ID映射。本地调用中,函数体是直接通过函数指针来指定的,调用函数时,编译器会自动调用它相应的函数指针。但在远程调用中,函数指针是不行的,因为两个进程的地址空间完全不一样。所以,在RPC中所有的函数都必须有自己的一个ID。这个ID在所有进程中都是唯一确定的。客户端在做远程过程调用时,必须附上这个ID。然后我们还需要在客户端和服务端分别维护一个 {函数 :Call ID} 的对应表。两者的表不一定需要完全相同,但相同的函数对应的Call ID必须相同。当客户端需要进行远程调用时,它就查该表,找出相应的Call ID,然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应函数的代码。

(2)序列化和反序列化。客户端如何将参数值传给远程的函数就成了一个问题在本地调用中,只需要把参数压到栈里,函数自己去栈里读即可。在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。甚至有时候客户端和服务端使用的都不是同一种语言(比如服务端用C++,客户端用Java或者Python)。这时需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。这个过程叫序列化和反序列化(也叫编码和解码)。同理,从服务端返回的值也需要序列化反序列化的过程。

(3)网络传输。远程调用往往用在网络上,客户端和服务端是通过网络连接的。所有的数据都需要通过网络传输,因此就需要有一个网络传输层。网络传输层需要把Call ID和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端。只要能完成这两者的,都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。尽管大部分RPC框架都使用TCP协议,但其实UDP也可以,而gRPC干脆就用了HTTP2,Java的Netty也属于这层的东西。

3.RPC调用过程是怎么样的

PRC的过程大概就是客户端让服务端做一件事,服务端将处理结果发给客户端。笔者认为可以用这样的一个例子来比喻:

客户端好比一个“老板”服务端好比一个“仓库”“老板”要从“仓库”要东西,“仓库”会自动将“老板”要的东西下单发送给“老板”,但是这样的过程是不是有点不太好管理,因为“老板”必须将注意力放到重大决策上,不能过多关注于如何去联系“仓库”,所以,“老板”需要有一个“秘书”来帮他处理这些事情,“仓库”也一样,每天有那么多“老板”都要东西,需要有一个“仓库管理员”来帮接收“老板”要什么东西,将东西打包发给“老板”

老板:

  • 客户端(client):服务调用发起方,也称为消费者。

秘书:

  • 客户端存根(client Stub):该程序运行在客户端所在的计算机机器上,主要用来存储要调用的服务器的地址,另外,该程序还负责将客户端请求远端服务器程序的数据信息打包成数据包,通过网络发送给服务端Stub程序;其次,还要接收服务端Stub程序发送的调用结果数据包,并解析返回给客户端。

仓库:

  • 服务端(server):远端的计算机机器上运行的程序,其中有客户端要调用的方法。

仓库管理员:

  • 服务端存根(server Stub):接收客户Stub程序通过网络发送的请求消息数据包,并调用服务端中真正的程序功能方法,完成功能调用;其次,将服务端执行调用的结果进行数据处理打包发送给客户端Stub程序。

 RPC调用过程为:

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

4.用一个简单的函数来看看这个过程是怎么样的

先看下目录结构:

handler(放处理函数)

package handlerconst HelloServiceName = "handler/HelloService"type NewHelloService struct {
}func (s *NewHelloService) Hello(request string, reply *string) error {//返回值是通过修改reply*reply = "hello," + requestreturn nil
}

服务端(仓库):server.go

package mainimport ("microServices/rpc_call/handler""microServices/rpc_call/server_proxy""net""net/rpc"
)func main() {//1.实例化一个serverlistener, _ := net.Listen("tcp", ":12345")//2.注册处理逻辑  handler_ = server_proxy.RegisterHelloService(&handler.NewHelloService{})//3.启动服务for {conn, _ := listener.Accept()go rpc.ServeConn(conn)}}

服务端存根(仓库管理员):server_proxy.go

package server_proxyimport ("microServices/rpc_call/handler""net/rpc"
)type HelloServicer interface {Hello(request string, reply *string) error
}func RegisterHelloService(srv HelloServicer) error {return rpc.RegisterName(handler.HelloServiceName, srv)
}
func main() {}

客户端(老板):client.go

package mainimport ("fmt""microServices/rpc_call/client_proxy"
)func main() {//1.建立连接client := client_proxy.NewHelloServiceStub("tcp", "localhost:12345")//1.只写业务逻辑,不关注每个函数的名称var reply stringerr := client.Hello("voyager", &reply)if err != nil {panic("调用失败")}fmt.Println(reply)
}

客户端存根(秘书):client_proxy.go

package client_proxyimport ("microServices/rpc_call/handler""net/rpc"
)type HelloServiceStub struct {*rpc.Client
}func NewHelloServiceStub(protol, address string) HelloServiceStub {conn, err := rpc.Dial(protol, address)if err != nil {panic("connect error!")}return HelloServiceStub{conn}
}func (c *HelloServiceStub) Hello(request string, reply *string) error {err := c.Call(handler.HelloServiceName+".Hello", request, reply)if err != nil {return err}return nil
}

梳理一下调用流程:

  • client端通过调用了本地的client_proxy的NewHelloServiceStub函数建立了连接
  • 然后client端要调用Hello这个函数。
  • client_proxy将hello的方法名,携带的参数做序列化操作,并打包成数据包,发出请求调用hello这个函数。
  • handler里面的函数实现了server_proxy里面的接口,所以在server端在注册处理逻辑的时候可以传入handler里面的函数。
  • server处理完返回给client端

为什么server_proxy里面要用interface?

因为如果不用的话,server_proxy和handler的耦合性太强, 也就是说handler里面的结构体改了,server_proxy也必须改,而server_proxy的RegisterHelloService方法关注的是方法是否被实现,在go里面interface刚好可以解决这个问题。


http://chatgpt.dhexx.cn/article/THoI97xs.shtml

相关文章

Dubbo的RPC调用流程

首先在客户端启动时会从注册中心拉去和订阅对应的服务列表,Cluster会把拉取到的服务列表聚合成一个cluster,每次RPC调用前会通过Directory#list获取providers地址(已经生成好的invoker列表),获取这些服务列表给后续路由和负载均衡使用,框架内…

RPC 调用原理

RPC调用原理 RPC原理 RPC(Remote Procedure Call)即远程过程调用,允许一台计算机调用另一台计算机上的程序得到结果,而代码中不需要做额外的编程,就像在本地调用一样。分布式的应用可以借助RPC来完成服务之间的调用。 RPC框架原理 在RPC框架…

RPC调用完整流程

RPC调用完整流程:   调用方持续把请求参数对象序列化成二进制数据,经过 TCP 传输到服务提供方;服务提供方从 TCP 通道里面接收到二进制数据;根据 RPC 协议,服务提供方将二进制数据分割出不同的请求数据,经…

RPC调用

1.RPC调用、本地调用和HTTP调用 网络调用一般是基于HTTP协议进行调用,RPC是使用TCP或者UDP协议进行调用,效率上回更好; 常用的RPC框架有Thrift、GRPC等; 2.RPC的调用时序图 1)调用方调用Proxy请求服务,并…

简单实现rpc调用

rpc调用 代码地址 查看代码 rpc称远程调用过程,在rpc调用过程中需要解决的几个问题。 代理协议序列化 技术选型 协议,我们使用java的socket编程套接字代理,使用java的动态代理序列化,使用java的原生的序列化。 rpc衍生出来…

Go rpc调用

RPC原理图 客户端Stub: 客户端存根服务端Stub: 服务端存根 RPC技术架构 服务端:server.go package mainimport ("math""net""net/http""net/rpc" )type MathUtil struct { }// 该方法向外暴露:提供计算圆形面…

rpc调用 java_RPC调用的简单实现

RPC调用流程 流程描述: 1.服务调用者发送请求(interface#method#args) 2.客户端进行StringEncode编码 3.数据写到服务提供者 4.服务提供者接受请求 5.将接收的包进行StringDecode解码 6.服务提供方调用对应api 7.服务提供方响应方法调用结果 8.服务提供方将结果集进行StringEn…

RPC调用和HTTP调用的区别

一.远程调用方式 无论是微服务还是分布式服务(都是SOA,都是面向服务编程),都面临着服务间的远程调用。那么服务间的远程调用方式有哪些呢? 常见的远程调用方式有以下几种: RPC:Remote Produce…

RPC远程调用通俗理解

先从一个案例来讲RPC 为了提升饭店的服务能力,饭店从一开始只有一个负责所有事情的厨师发展成有厨师、切菜师、备菜师等多个角色。在饭店只有一个厨师的时候,厨师想要做出一道美味的番茄炒蛋的时候,他需要自己洗番茄、切番茄、打鸡蛋、炒菜。…

RPC通信基本原理 -- 浅析RPC远程过程调用基本原理

一、RPC基本概念 1.1、RPC简介 RPC 的全称是 Remote Procedure Call是一种进程间通信方式。RPC只是一个概念 而不是具体的协议或框架。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远…

RPC服务调用

RPC服务调用 一、RPC实现原理深入分析1、RPC的定义(1)RPC作用(2)RPC核心组成(3)RPC调用方式 二、精简版RPC调用代码实现1、场景2、接口设计3、序列化协议 最近在做调用系统相关的服务,采用主从的…

远程过程调用(RPC)详解

本文介绍了什么是远程过程调用(RPC),RPC 有哪些常用的方法,RPC 经历了哪些发展阶段,以及比较了各种 RPC 技术的优劣。 什么是 RPC RPC 是远程过程调用(Remote Procedure Call)的缩写形式,Birrell 和 Nels…

Simple Schnorr Multi-Signatures with Applications to Bitcoin 学习笔记

1. 引言 Blockstream团队2018年论文《Simple Schnorr Multi-Signatures with Applications to Bitcoin》。 对应的代码实现: https://github.com/KZen-networks/multi-party-schnorr https://github.com/lovesh/signature-schemes 论文要点: MuSig——…

【密码学原理】数字签名(ElGamal签名,Schnorr签名,椭圆曲线签名,RSA-PSS签名)

数字签名是公钥密码学发展过程中最重要的概念之一,产生和使用数字签名过程的一般模型如图所示 消息认证可以保护消息交换双方不受第三方的攻击,但是不能处理通信双方自身发生的攻击。例如对下图中的某种方式进行攻击,考虑两种情况&#xff1a…

Schnorr签名java实现

Schnorr签名(模指数)的实现java 1、算法描述2、算法的实现(java) 和ElGama数字签名一样,Schnorr数字签名方案也是基于离散对数。 Schnorr数字签名主要工作不依赖于消息,生成签名过程与消息相关的部分需要进…

Blind Schnorr Signature

1. 引言 前序博客有: 盲签名 blind signature Blind Schnorr Signature交互签名Demo见: Blind Schnorr Signature Interactive Demo 参考资料 [1] Schnorr Applications: Blind Signatures

本体技术视点 | 为什么 BIP - 340 选择引入 Schnorr 签名算法?

引言 备受瞩目的技术升级 Taproot 软分叉将于最近激活,激活高度为 709632,距今已不足 500 个区块。此次升级包括三个改进提案,分别是 BIP - 340、BIP - 341 和 BIP - 342。其中,BIP - 340 引入了 Schnorr 签名,BIP - …

【密码学】Schnorr认证,Schnorr签名,安全性证明

Schnorr是Sigma协议的实例,从Schnorr 认证协议和Schnor签名两个部分来介绍。在前面关于认证协议的讲述中,用到了一些特定的符号,这里会继续使用。 首先认识Schnorr认证协议,定义如图所示 Schnorr认证协议和Schnorr签名方案的安全…

零知识证明系列之二——Schnorr协议

​Schnorr协议简介 Schnorr协议是由德国数学家和密码学家Claus-Peter Schnorr在1991年提出,是一种基于离散对数难题的知识证明机制。Schnorr本质上是一种零知识的技术,即Prover声称知道一个密钥x的值,通过使用Schnorr加密技术,可…

Schnorr 签名方案和 BLS 签名方案的全面对比

原文:https://medium.com/cryptoadvance/bls-signatures-better-than-schnorr-5a7fe30ea716https://medium.com/cryptoadvance/how-schnorr-signatures-may-improve-bitcoin-91655bcb4744 Schnorr 签名算法最初是由德国密码学家 Claus Schnorr 于 2008 年提出的&…