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刚好可以解决这个问题。

用一个简单的函数来理一下RPC调用过程相关推荐

  1. python绘制一个简单的函数图像使用到了matplotlib库和numpy库

    文章目录 效果展示: 视频链接 实现的思想 使用到的函数包 图片一对应的代码展示 图片二 对应的代码展示 注意事项 效果展示: 视频链接 python绘制一个简单的函数图像(B站视频) 实现的思想 其 ...

  2. php mockery单元测试,php - 使用Mockery在模型中测试一个简单的函数 - SO中文参考 - www.soinside.com...

    我完全擅长使用嵌入在Laravel中的Mockery.我很难测试一个简单的模型函数,它增加了引用的一部分,无论我传递给测试结果的值是否正确,即使它应该失败.我想我在某处犯了错误或者我不懂文档.谢谢你的 ...

  3. php随机数怎么获取?一个简单的函数就能生成

    小美女建了一个站,有些页面相似度比较高,想添加一些字段来实现差异化,比如用php随机数生成从10到100之间随机一个数字.其实会php的朋友几十个字符就能实现了,如下代码所示,简单吧?10代表最小值, ...

  4. java 调用一个抛出异常的函数,Java VS C++(14) 调用可能抛出异常的函数

    调用可能抛出异常的函数 (1)java看是否是受检异常,如果是必须try catch, 如果是非受检异常,则不用 try catch void test()throws MyException; (2 ...

  5. python如何编写数据库_如何在几分钟内用Python编写一个简单的玩具数据库

    python如何编写数据库 MySQL, PostgreSQL, Oracle, Redis, and many more, you just name it - databases are a re ...

  6. R语言笔记6:在R中写一些简单的函数、functions基础和作用域

    R语言基础系列: 1数据类型(向量.数组.矩阵. 列表和数据框) 2读写数据所需的主要函数.与外部环境交互 3数据筛选--提取对象的子集 4向量.矩阵的数学运算 5控制结构 Your first R ...

  7. 初学__Python——Python 最简单的函数—用 lambda 声明函数

    用 lambda 表达式来声明函数,是Python中一类比较特殊的声明函数的方式,lambda 来源于 LISP 语言,使用 lambda 可以声明一个匿名函数.所谓匿名函数是指所声明的函数没有函数名 ...

  8. VS2017生成一个简单的DLL文件 和 LIB文件——C语言

    下面我们将用两种不同的姿势来用VS2017生成dll文件(动态库文件)和lib文件(静态库文件),这里以C语言为例,用最简单的例子,来让读者了解如何生成dll文件(动态库文件) 生成动态库文件 姿势一 ...

  9. 3-4:一个简单的HTTP服务器

    文章目录 HTTP服务器 HTTP服务器 我们使用浏览器请求某一个网页时,浏览器会向相应的服务器发送请求,服务器得到请求后会将报文传送回来,然后浏览器解析,就形成了我们所看见到的网页 这里我们可以写一 ...

  10. [nRF51822] 1、一个简单的nRF51822驱动的天马4线SPI-1.77寸LCD彩屏DEMO

    最近用nRF51822写了个天马4线SPI的1.77寸LCD彩屏驱动,效果如下: 屏幕的规格资料为:http://pan.baidu.com/s/1gdfkr5L 屏幕的驱动资料为:http://pa ...

最新文章

  1. 使用CURL构建爬虫,抓取百度百科内容
  2. scrapy (2)下载图片及存储信息
  3. VS2017 Cordova Ionic2 移动开发-环境搭建
  4. 浅谈数据分析中的“暗物质”
  5. sql server 修改IDENTITY 字段 的统计当前值
  6. 音视频技术开发周刊 | 156
  7. JAVA企业级应用TOMCAT实战视频课程
  8. 锐起无盘辅服务器的作用,锐起无盘特殊功用为网吧带来更多收益
  9. Pandas 文本数据方法 join( )
  10. excel同一单元格怎么换行_自动换行还是强制换行?还有一键批量换行等着你
  11. mp3转换html5,五个免费在线mp3音频音乐编辑转换网站,实用的音频编辑软件
  12. Ubuntu中禁止与使能鼠标中键的复制功能(vscode出现莫名其妙的多余的代码段)
  13. <img> 的 title 和 alt 有什么区别
  14. win10计算机不分区,win10系统自带无损分区且数据不丢失的处理方案
  15. 用matlab解决多重共线性问题,几种关于多重共线性回归问题的解决方法
  16. 2022年全球市场次氯酸钠总体规模、主要生产商、主要地区、产品和应用细分研究报告
  17. Winform运行后,界面尺寸与设计时不一样
  18. IDEA工具栏tools新增Push按钮(其他类推)
  19. 【数据结构】史上最好理解的红黑树讲解,让你彻底搞懂红黑树
  20. 小甲鱼《零基础学习Python》课后笔记(二十八):文件——因为懂你,所以永恒

热门文章

  1. 一些有用软件收藏(更新中)
  2. c语言代码量统计工具
  3. Matlab:求出n边凹多边形内角
  4. 2017年苏大计算机考研872真题及解析
  5. 信息系统项目管理:如何制定项目章程?
  6. 关于HTML页面跳转的5种方法分享。
  7. java rxtx下载_1、下载64位rxtx for java 链接:http://fizzed.com/oss/rxtx-for-java2、下载下来的包解压后按照说明放到JAV...
  8. 如何将idea代码提交到远程仓库
  9. 今日制造怎么安装solidworks插件_PS插件安装后出现了登陆界面,无法使用怎么解决?保证一招搞定...
  10. java int转byte_JAVA中怎么将int数据转换为byte数据?