RPC原理解析

什么是RPC

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP/IP或UDP,为通信程序之间携带信息数据。RPC将原来的本地调用转变为调用远端的服务器上的方法,给系统的处理能力和吞吐量带来了近似于无限制提升的可能。在OSI网络通信模型中,RPC跨域了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

RPC架构

一个完整的RPC架构里面包含了四个核心的组件,分别是Client,Client Stub,Server以及Server Stub,这个Stub可以理解为存根。

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

客户端存根(Client Stub):存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。

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

服务端存根(Server Stub):接收客户端发送过来的消息,将消息解包,并调用本地的方法。

RPC调用过程



(1) 客户端(client)以本地调用方式(即以接口的方式)调用服务;

(2) 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);

(3) 客户端通过sockets将消息发送到服务端;

(4) 服务端存根( server stub)收到消息后进行解码(将消息对象反序列化);

(5) 服务端存根( server stub)根据解码结果调用本地的服务;

(6) 本地服务执行并将结果返回给服务端存根( server stub);

(7) 服务端存根( server stub)将返回结果打包成消息(将结果消息对象序列化);

(8) 服务端(server)通过sockets将消息发送到客户端;

(9) 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);

(10) 客户端(client)得到最终结果。

RPC的目标是要把2、3、4、7、8、9这些步骤都封装起来。

注意:无论是何种类型的数据,最终都需要转换成二进制流在网络上进行传输,数据的发送方需要将对象转换为二进制流,而数据的接收方则需要把二进制流再恢复为对象。

RPC框架

RPC是分布式系统/应用中应用最为广泛的一种协议,最常见的RPC框架为grpc、Dubble等,下面以Dubble为示例,分析下Dubble框架中RPC的具体使用和实现原理;

Dubble中RPC实现分析

	//客户端
@Component
public class HelloClient { @Reference // dubbo 注解
private HelloService helloService; public String hello() {
return helloService.hello("World");
}
} // 服务端
@Service // dubbo 注解
@Component
public class HelloServiceImpl implements HelloService { @Override
public String hello(String name) {
return "Hello " + name;
}
}

示例分析

在客户端,我们可以通过 @Reference 注解,获得一个实现了 HelloServicer 这个接口的对象,我们的业务代码只要调用这个对象的方法,就可以获得结果。对于客户端代码来说,调用就是 helloService 这个本地对象,但实际上,真正的服务是在远程的服务端进程中实现的。

再来看服务端,在服务端我们的实现类 HelloServiceImpl,实现了 HelloService 这个接口。然后,我们通过 @Service 这个注解(注意,这个 @Service 是 Dubbo 提供的注解,不是 Spring 提供的同名注解),在 Dubbo 框架中注册了这个实现类 HelloServiceImpl。在服务端,我们只是提供了接口 HelloService 的实现,并没有任何远程调用的实现代码。

对于业务代码来说,无论是客户端还是服务端,除了增加了两个注解以外,和实现一个进程内调用没有任何区别。Dubbo 看起来就像把服务端进程中的实现类“映射”到了客户端进程中一样。接下来我们一起来看一下,Dubbo 这类 RPC 框架是如何来实现调用远程服务的。

实现分析

在客户端,业务代码得到的 HelloService 这个接口的实例,并不是我们在服务端提供的真正的实现类 HelloServiceImpl 的一个实例。它实际上是由 RPC 框架提供的一个代理类的实例。这个代理类有一个专属的名称,叫“桩(Stub)”。

在不同的 RPC 框架中,这个桩的生成方式并不一样,有些是在编译阶段生成的,有些是在运行时动态生成的,这个和编程语言的语言特性是密切相关的,所以在不同的编程语言中有不同的实现,这部分很复杂,可以先不用过多关注。我们只需要知道这个桩它做了哪些事儿就可以了。

我们知道,HelloService 的桩,同样要实现 HelloService 接口,客户端在调用 HelloService 的 hello 方法时,实际上调用的是桩的 hello 方法,在这个桩的 hello 方法里面,它会构造一个请求,这个请求就是一段数据结构,请求中包含两个重要的信息:

  1. 请求的服务名,在我们这个例子中,就是 HelloService#hello(String),也就是说,客户端调用的是 HelloService 的 hello 方法;
  2. 请求的所有参数,在我们这个例子中,就只有一个参数 name, 它的值是“World”。

    然后,它会把这个请求发送给服务端,等待服务的响应。这个时候,请求到达了服务端,然后我们来看服务端是怎么处理这个请求的。

服务端的 RPC 框架收到这个请求之后,先把请求中的服务名解析出来,然后,根据这个服务名找一下,在服务端进程中,有没有这个服务名对应的服务提供者。

在这个例子的服务端中,由于我们已经通过 @Service 注解向 RPC 框架注册过 HelloService 的实现类,所以,RPC 框架在收到请求后,可以通过请求中的服务名找到 HelloService 真正的实现类 HelloServiceImpl。找到实现类之后,RPC 框架会调用这个实现类的 hello 方法,使用的参数值就是客户端发送过来的参数值。服务端的 RPC 框架在获得返回结果之后,再将结果封装成响应,返回给客户端。

客户端 RPC 框架的桩收到服务端的响应之后,从响应中解析出返回值,返回给客户端的调用方。这样就完成了一次远程调用。我把这个调用过程画成一张图放在下面,你可以对着这张图再消化一下上面的流程。



在上面的这个调用流程中,客户端是如何找到服务端的呢?在 RPC 框架中,这部分的实现原理和消息队列的实现是完全一样的,都是通过一个 NamingService来解决的。

在 RPC 框架中,这个 NamingService 一般称为注册中心。服务端的业务代码在向 RPC 框架中注册服务之后,RPC 框架就会把这个服务的名称和地址发布到注册中心上。客户端的桩stud在调用服务端之前,会向注册中心请求服务端的地址,请求的参数就是服务名称,也就是我们上面例子中的方法签名 HelloService#hello,注册中心会返回提供这个服务的地址,然后客户r55555767 端再去请求服务端。

有些 RPC 框架,比如 gRPC,是可以支持跨语言调用的。它的服务提供方和服务调用方是可以用不同的编程语言来实现的。比如,我们可以用 Python 编写客户端,用 Go 语言来编写服务端,这两种语言开发的服务端和客户端仍然可以正常通信。这种支持跨语言调用的 RPC 框架的实现原理和普通的单语言的 RPC 框架并没有什么本质的不同。

我们可以再回顾一下上面那张调用的流程图,如果需要实现跨语言的调用,也就是说,图中的客户端进程和服务端进程是由两种不同的编程语言开发的。其实,只要客户端发出去的请求能被服务端正确解析,同样,服务端返回的响应,客户端也能正确解析,其他的步骤完全不用做任何改变,不就可以实现跨语言调用了吗?

在客户端和服务端,收发请求响应的工作都是 RPC 框架来实现的,所以,只要 RPC 框架保证在不同的编程语言中,使用相同的序列化协议,就可以实现跨语言的通信。另外,为了在不同的语言中能描述相同的服务定义,也就是我们上面例子中的 HelloService 接口,跨语言的 RPC 框架还需要提供一套描述服务的语言,称为 IDL(Interface description language)。所有的服务都需要用 IDL 定义,再由 RPC 框架转换为特定编程语言的接口或者抽象类。这样,就可以实现跨语言调用了。

讲到这里,RPC 框架的基本实现原理就很清楚了,可以看到,实现一个简单的 RPC 框架并不是很难,这里面用到的绝大部分技术,包括:高性能网络传输、序列化和反序列化、服务路由的发现方法等,与消息队列实现原理中的内容基本类似。

总结

学习一个东西最好的方法就是动手实践,后面文章中我们会手动实现一个RPC框架,并解析一下现存优秀的RPC框架

一文搞懂RPC原理的更多相关文章

  1. 一文搞懂 ThreadLocal 原理

    当多线程访问共享可变数据时,涉及到线程间同步的问题,并不是所有时候,都要用到共享数据,所以就需要线程封闭出场了. 数据都被封闭在各自的线程之中,就不需要同步,这种通过将数据封闭在线程中而避免使用同步的 ...

  2. 一文搞懂 Prometheus 的直方图

    原文链接:一文搞懂 Prometheus 的直方图 Prometheus 中提供了四种指标类型(参考:Prometheus 的指标类型),其中直方图(Histogram)和摘要(Summary)是最复 ...

  3. Web端即时通讯基础知识补课:一文搞懂跨域的所有问题!

    本文原作者: Wizey,作者博客:http://wenshixin.gitee.io,即时通讯网收录时有改动,感谢原作者的无私分享. 1.引言 典型的Web端即时通讯技术应用场景,主要有以下两种形式 ...

  4. 一文搞懂所有Java集合面试题

    Java集合 刚刚经历过秋招,看了大量的面经,顺便将常见的Java集合常考知识点总结了一下,并根据被问到的频率大致做了一个标注.一颗星表示知识点需要了解,被问到的频率不高,面试时起码能说个差不多.两颗 ...

  5. 一文搞懂指标采集利器 Telegraf

    作者| 姜闻名 来源|尔达 Erda 公众号 ​ 导读:为了让大家更好的了解 MSP 中 APM 系统的设计实现,我们决定编写一个<详聊微服务观测>系列文章,深入 APM 系统的产品.架构 ...

  6. 一文搞懂RAM、ROM、SDRAM、DRAM、DDR、flash等存储介质

    一文搞懂RAM.ROM.SDRAM.DRAM.DDR.flash等存储介质 存储介质基本分类:ROM和RAM RAM:随机访问存储器(Random Access Memory),易失性.是与CPU直接 ...

  7. 基础篇|一文搞懂RNN(循环神经网络)

    基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...

  8. 一文搞懂vim复制粘贴

    转载自本人独立博客https://liushiming.cn/2020/01/18/copy-and-paste-in-vim/ 概述 复制粘贴是文本编辑最常用的功能,但是在vim中复制粘贴还是有点麻 ...

  9. 三文搞懂学会Docker容器技术(中)

    接着上面一篇:三文搞懂学会Docker容器技术(上) 6,Docker容器 6.1 创建并启动容器 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] --na ...

随机推荐

  1. ios多线程开发基础

    多线程编程:下载数据时,开辟子线程,减少阻塞时间,和主线程并发运行,提升用户体验 1.Thread 1>新建Thread对象,带一selector方法,调用start方法,开启子线程 2> ...

  2. .NET 6 Preview 6 正式发布: 关注网络开发

    微软.NET 团队的项目经理在博客上发布了.NET 6 Preview 6,  在候选发布阶段之前的倒数第二个预览版,也就是8月份还会发布一个Preview 7,9月份开始进入RC,两个候选版本将专注 ...

  3. P4480 「BJWC2018」「网络流与线性规划24题」餐巾计划问题

    刷了n次用了奇淫技巧才拿到rk1,亥 这道题是网络流二十四题中「餐巾计划问题」的加强版. 于是怀着试一试的心情用费用流交了一发: 哇塞,过了9个点!(强烈谴责出题人用*造数据 下面是费用流解法简述: ...

  4. Tomcat网站根目录设置

    直接将war放入到webapps目录下 修改server.xml文件,在Host节点下添加如下代码 <Context path="/" docBase="web&q ...

  5. [刘阳Java]_Web前端入门级练习_迅雷首页第一屏设计

    今天接着上一篇文章<Web前端入门级练习_迅雷官宣网设计>正式开始迅雷首页第一版的设计.如果完成,则最终的效果图如下 第一步:先完成logo部分的设计 logo设计,我们会使用CSS的定位 ...

  6. Appium - adb monkey参数(三)

    monkey 参数 1.常规类参数 A.帮助类参数 monkey -h B.日志级别  $ adb shell monkey -v <event-count> . 2.事件类参数 A .执 ...

  7. spring-1-spring介绍和IOC容器开发

    一.介绍 1.版本 2.下载(jar包依赖) 下载 所以搜索:https://repo.spring.io/release/org/springframework/spring/ 文件分配 maven ...

  8. 记一次.Net5接入支付宝SDK的小插曲

    由于业务需求,在项目里面要接入支付宝的支付功能,于是在github上找到了支付宝的官方sdk:https://hub.fastgit.org/alipay/alipay-easysdk 先说问题: 在 ...

  9. 微信小程序云开发-数据库-查询满足条件的数据

    一.查询价格大于10的商品 1.wxml文件 2.js文件 where条件语句:.where({price:db.command.gt(10)}) 3.查询结果 二.查询价格大于等于10的商品 js文 ...

  10. bs4爬取笔趣阁小说

    参考链接:https://www.cnblogs.com/wt714/p/11963497.html 模块:requests,bs4,queue,sys,time 步骤:给出URL--> 访问U ...