RPC原理(1)之深入RPC原理简介

article/2025/9/26 19:03:04

一、RPC调用原理图

下面这张图是我们微服务一次Http调用请求图:

首先在请求的过程中我们知道是有三次握手,四次挥手的流程,具体流程如下:

1.浏览器请求服务器(订单服务),请求建立连接,首先客户端向服务器端发送一段 TCP 报文
2.服务器(订单服务)相应浏览器,可以建立连接,并且询问浏览器是否马上建立连接。
3.浏览器相应服务器(订单服务),可以建立连接。-----开始传输数据------------###下面要断开连接就进入四次挥手环节
1.浏览器向服务器(订单服务)发起请求,需要断开连接(分手)
2.服务器(订单服务)相应浏览器,说我收到断开请求了,但是需要在想一下(一般是数据传输)
3.服务器(订单服务)接受完数据之后,向浏览器发送请求,说可以断开连接了(想好了,可以分手了)
4.浏览器接受到服务器(订单服务),回复服务器,我断开连接了。

1.1RPC概述

RPC(Remote Procedure Call Protocol)远程过程调用协议。RPC的主要功能是让构建分布式计算更加容易,再提供强大的远程调用能力的同时而不损失本地调用的语义简洁性。为实现该目标,RPC框架需要提供一套透明的调用机制,使使用者不用显示的区分本地调用还是分布式调用。

RPC的优点:

  • 分布式设计
  • 部署灵活
  • 解耦服务
  • 扩展性强

RPC框架的优势:

  • RPC框架一般使用长连接,不必要每次通信都三次握手、四次挥手。
  • RPC框架一般有注册中心,有丰富的监控管理、发布、下线接口、动态扩展等。对调用方来说是无感知的、统一化的操作、协议私密,安全性高。
  • RPC更简单内容更小、效率更高、服务化架构、服务化治理、RPC框架是一个强力的支撑。
  • RPC框架基于TCP实现,也可以基于Http2实现。

1.2RPC框架

        主流的RPC框架:

  • Dubbo:国内最早开源的RPC框架,由阿里巴巴公司开发并与2011年对外发布,不过仅支持Java语言。
  • Motan:新浪微博内部使用的RPC框架,与2016年对外开源,仅支持Java语言。
  • Tars:腾讯内部使用的RPC框架,与2017年对外开源,仅支持C++语言。
  • Spring Cloud:国外 Pivotal 公司 2014 年对外开源的 RPC 框架,提供了丰富的生态组件。
  • gRPC:Goolge与2015年对外开源的跨语言rpc框架,2007年贡献给了Apache基金,成为了Apache开源项目之一,支持多种语言。

1.3应用场景

        应用举例:

  • 分布式操作系统的进程间通讯:进程间通讯是操作系统必须提供的基本设施之一,分布式操作系统必须提供分布于异构的结点机上进 程间的通讯机制,RPC是实现消息传送模式的分布式进程间通讯方式之一。
  • 构造分布式设计的软件环境:由于分布式软件设计,服务与环境的分布性, 它的各个组成成份之间存在大量的交互和通讯, RPC是 其基本的实现方法之一。Dubbo分布式服务框架基于RPC实现,Hadoop也采用了RPC方式实现客 户端与服务端的交互。
  • 远程数据库服务:在分布式数据库系统中,数据库一般驻存在服务器上,客户机通过远程数据库服务功能访问数据库 服务器,现有的远程数据库服务是使用RPC模式的。例如,Sybase和Oracle都提供了存储过程机 制,系统与用户定义的存储过程存储在数据库服务器上,用户在客户端使用RPC模式调用存储过 程。
  • 分布式应用程序设计:RPC机制与RPC工具为分布式应用程序设计提供了手段和方便, 用户可以无需知道网络结构和协议细 节而直接使用RPC工具设计分布式应用程序。
  • 分布式程序调试:RPC可用于分布式程序的调试。使用反向RPC使服务器成为客户并向它的客户进程发出RPC,可以 调试分布式程序。例如,在服务器上运行一个远端调试程序,它不断接收客户端的RPC,当遇到一 个调试程序断点时,它向客户机发回一个RPC,通知断点已经到达,这也是RPC用于进程通讯的例 子。

二、深入RPC原理

        2.1设计与调用原理

 

        具体调用过程:

  1. 服务消费者(client客户端)通过本地调用的方式调用服务。
  2. 客户端存根(client stub)接收到请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体。
  3. 客户端存根(client stub)找到远程的服务地址,并且将消息通过网络发送给服务端。
  4. 服务端存根(server stub)收到消息后进行解码(反序列化操作)。
  5. 服务端存根(server stub)根据解码结果调用本地的服务进行相关处理。
  6. 本地服务执行具体的业务逻辑并将处理结果返回给服务端存根(server stub)。
  7. 服务端存根(server stub)将返回的结果重新打包成消息(序列化),并通过网络发送到消费方。
  8. 客户端存根(client stub)接收到消息,并进行解码(反序列化)。
  9. 服务消费方得到最终的结果。

所涉及到的技术:

  1. 动态代理:生成Client stub(客户端存根)和Server Stub(服务端存根)的时候需要用到java动态代码技术。
  2. 序列化:在网络中,所有的数据都将会转化为字节进行传送,需要对这些参数进行序列化和反序列化操作。目前主流的开源序列化框架有Kryo、fastJson、Hessian、Protobuf等。
  3. NIO通信:Java提供了NIO的解决方案,Java7也提供了更优秀的NIO.2支持。可以采用Netty或者mina框架来解决数据传输的问题。开源的RPC框架Dubbo就是采用NIO通信,集成netty、mina、grizzly。
  4. 服务注册中心:通过注册中心,让客户端连接调用服务端所发布的服务。主流的注册中心组件:redis、Nacos、Zookeeper、Consul、Etcd。Dubbo采用的是ZooKeeper提供服务注册与发现功能。
  5. 负载均衡:在高并发的场景下,需要多个节点或者集群来提升整体的吞吐能力。
  6. 健康检查:健康检查包括,客户端心跳和服务主动探测两种方式。

2.2 RPC深入解析 

2.2.1序列化技术

  •  序列化的作用:

        在网络传输中,数据必须采用二进制形式,所以在RPC调用过程中,需要采用序列化技术,对入参对象和返回值对象进行序列化和反序列化。

  • 序列化原理:

        自定义二进制协议来实现序列化:

         

  • 序列化处理要素:
  1. 解析效率:序列化协议应该首要考虑的因素,像json/xml解析起来比较耗时,需要解析dom树,二进制自定义协议解析起来效率要快很多。
  2. 压缩率:同样一个对象,xml/json传输起来有大量的标签冗余信息,信息有消息低,二进制自定义协议占用的空间相对来说会小很多。
  3. 扩展性与可调式性:xml/json可读性会比二进制协议好很多,并且通过网络抓包是可以直接读取,二进制则需要反序列化才能查看能内容。
  4. 跨语言:有些序列化协议与开发语言紧密相关的,例如dobbo的Hessian序列化协议只能支持Java的RPC调用。
  5. 通用性:xml/json非常通用,都有很好的第三方解析库,各个语言解析起来都十分方便,二进制数据的处理方面也有Protobuf和Hessian等插件,在做设计的时候尽量做到较好的通用性。
  • 常用的序列化技术

        1.JDK原生序列化,代码如下:

public class JDKSerialization {public static void serialize() throws Exception {//将序列化后的数据存入到D:/TestCode/tradeUser.clazz中String basePath =  "D:/TestCode/";FileOutputStream fos = new FileOutputStream(basePath + "tradeUser.clazz");//创建tradeUser对象TradeUser tradeUser = new TradeUser();tradeUser.setName("Mirson");//将tradeUser写入到tradeUser.clazz中ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(tradeUser);oos.flush();oos.close();//读取D:/TestCode/tradeUser.clazzFileInputStream fis = new FileInputStream(basePath + "tradeUser.clazz");ObjectInputStream ois = new ObjectInputStream(fis);//将读取的数据反序列化到对象中TradeUser deTradeUser = (TradeUser) ois.readObject();ois.close();System.out.println("=== 反序列化结果 ==== ");System.out.println(deTradeUser);}public static void main(String[] args) throws Exception {serialize();}
}@Data
public class TradeUser implements Serializable {/*** 用户编号*/private String userNo;/*** 用户名称*/private String name;/*** 用户密码*/private String userPwd;/*** 电话号码*/private String phone;/*** 公司ID*/private Long companyId;/*** 邮箱*/private String email;/*** 地址*/private String address;/*** 最近一次用户登陆IP*/private String lastLoginIp;/*** 最近一次登陆时间*/private Date lastLoginTime;/*** 状态(0:有效, 1:锁定, 2:禁用)*/private int status;/*** 创建时间*/private Date createTime;}
  1. 在Java中,序列化必须要实现java.io.Serializable接口。
  2. 通过ObjectOutputStream和ObjectInputStream对象进行序列化以及反序列化操作。
  3. 虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的点事两个类的序列化ID是否一致(也就是在代码中定于的序列ID private static final long serialVersionUID);
  4. 序列化并不会保存静态变量。
  5. 想要父类对象也序列化,就需要父类也实现Serializable接口;
  6. Transient关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient变量的值被设为初始值,如基本类型int为0,封装对象型则为null;
  7. 服务器给客户端发哦送你个序列化对象数据并非加密的,如果对象中有一些敏感数据比如密码等,那么在对密码字段序列化之前,最好做加密处理,这样子可以一定程度保证序列化对象的数据安全。

        2.json序列化

        一般在HTTP协议的RPC框架通信中,会选择JSON方式,因为JSON有较好的扩展性、可读性和通用性。

        缺陷:JSON序列化占用空间开销较大,没有JAVA的强类型区分,需要通过反射解决,解析效率和压缩率都较差。

        如果对并发和性能要求较高,或者是传输数据量较大的场景,不建议采用JSON序列化方式。

        3.Hessian2序列化

        Hessian是一个动态类型,二进制序列化,并且支持跨语言特性的序列化框架。Hessian性能上要比JDK、JSON序列化高效很多,并且生成的字节数更小,有非常好的兼容性和稳定性,所以Hessian更加适合作为RPC框架远程通信的序列化协议。

        代码示例:

public class Hessian2Serialization {public static void main(String[] args) throws Exception {serialize();}public static void serialize() throws Exception {TradeUser tradeUser = new TradeUser();tradeUser.setName("Mirson");tradeUser.setUserNo("100001");//tradeUser对象序列化处理ByteArrayOutputStream bos = new ByteArrayOutputStream();Hessian2Output output = new Hessian2Output(bos);output.writeObject(tradeUser);output.flushBuffer();byte[] data = bos.toByteArray();System.out.println("=== 序列化结果 ==== ");System.out.println(data);bos.close();//tradeUser对象反序列化处理ByteArrayInputStream bis = new ByteArrayInputStream(data);Hessian2Input input = new Hessian2Input(bis);TradeUser deTradeUser = (TradeUser) input.readObject();input.close();System.out.println("=== 反序列化结果 ==== ");System.out.println(deTradeUser);}
}

        Dobbo Hessian Lite序列化流程:

Dubbo Hessian Lite反序列化流程:

 

        Hessian自身也存在一些缺陷,大家在使用过程中要注意:

  • 对Linked系列对象不支持,比如LinkedHashMap、LinkedHashSet等,但可以通过CollectionSerializer类修复。
  • Local类不支持,可以通过拓展ContextSerizlizerFactory类修复。
  • Byte/Short在反序列化的时候会转成Integer。

Dubbo2.7.3通讯序列化源码实现分析:

        

 

        ExchangeCodec的 encode 方法:
    public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {if (msg instanceof Request) {this.encodeRequest(channel, buffer, (Request)msg);} else if (msg instanceof Response) {this.encodeResponse(channel, buffer, (Response)msg);} else {super.encode(channel, buffer, msg);}}

        反序列化流程:

        源码如下:ExchangeCodec的decode方法

    public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {int readable = buffer.readableBytes();byte[] header = new byte[Math.min(readable, 16)];buffer.readBytes(header);return this.decode(channel, buffer, readable, header);}protected Object decodeBody(Channel channel, InputStream is, byte[] header) throws IOException {byte flag = header[2];byte proto = (byte)(flag & 31);long id = Bytes.bytes2long(header, 4);if ((flag & -128) == 0) {Response res = new Response(id);if ((flag & 32) != 0) {res.setEvent(true);}byte status = header[3];res.setStatus(status);try {ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto);if (status == 20) {Object data;if (res.isHeartbeat()) {data = this.decodeHeartbeatData(channel, in);} else if (res.isEvent()) {data = this.decodeEventData(channel, in);} else {data = this.decodeResponseData(channel, in, this.getRequestData(id));}res.setResult(data);} else {res.setErrorMessage(in.readUTF());}} catch (Throwable var12) {res.setStatus((byte)90);res.setErrorMessage(StringUtils.toString(var12));}return res;} else {Request req = new Request(id);req.setVersion(Version.getProtocolVersion());req.setTwoWay((flag & 64) != 0);if ((flag & 32) != 0) {req.setEvent(true);}try {ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto);Object data;if (req.isHeartbeat()) {data = this.decodeHeartbeatData(channel, in);} else if (req.isEvent()) {data = this.decodeEventData(channel, in);} else {data = this.decodeRequestData(channel, in);}req.setData(data);} catch (Throwable var13) {req.setBroken(true);req.setData(var13);}return req;}}

        4.Protobuf序列化

        Protobuf是Google推出的开源的序列库,它是一种轻便、高效的结构化数据存储格式,可以用于结构化数据序列化,支持Java、Python、C++、Go等多种语言,

        Protobuf使用的时候需要定义IDL(inteface description language),然后使用不同语言的IDL编译器,生成序列化工具类,它具备一下有点:

  • 压缩比高,体积小,序列化体积相比JSON、Hessian小很多;
  • IDL能清晰的描述语义,可以帮助保证应用程序之间的类型不会丢失,无需类似XML解析器;
  • 序列化反序列化速度很快,不需要通过反射获取类型;
  • 消息格式的拓展、升级和兼容性都不错,可以做到向后兼容;

        脚本示例:

// 定义Proto版本
syntax = "proto3";
// 是否允许生成多个JAVA文件
option java_multiple_files = false;
// 生成的包路径
option java_package = "com.itcast.rpc.samples.serial.proto";
// 生成的JAVA类名
option java_outer_classname = "TradeUserProto";// 预警通知消息体
message TradeUser {/*** 用户ID*/int64 userId = 1 ;/*** 用户名称*/string userName = 2 ;
}

        代码操作:

public class ProtoSerialization {public static void serialize() throws Exception{// 创建TradeUser的Protobuf对象TradeUserProto.TradeUser.Builder builder = TradeUserProto.TradeUser.newBuilder();builder.setUserId(100001);builder.setUserName("Mirson");//将TradeUser做序列化处理TradeUserProto.TradeUser msg = builder.build();byte[] data = msg.toByteArray();System.out.println("=== 序列化结果 ==== ");System.out.println(data);//反序列化处理, 将刚才序列化的byte数组转化为TradeUser对象TradeUserProto.TradeUser deTradeUser = TradeUserProto.TradeUser.parseFrom(data);System.out.println("=== 反序列化结果 ==== ");System.out.println(deTradeUser);}
}


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

相关文章

RPC原理简述

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

RPC核心原理详解

什么是RPC? RPC的全称是Remote Procedure Call,即远程过程调用。简单解读字面上的意思,远程肯定是指要跨机器而非本机,所以需要用到网络编程才能实现,但是不是只要通过网络通信访问到另一台机器的应用程序&#xff0c…

RPC原理

RPC就是远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。 原理 一个完整的RPC主要包括三部分: 服务注册中心(Registry),负责将本地服务发布成远程服务,管理远…

从零开始实现RPC框架 - RPC原理及实现

RPC概述 RPC(Remote Procedure Call)即远程过程调用,允许一台计算机调用另一台计算机上的程序得到结果,而代码中不需要做额外的编程,就像在本地调用一样。 现在互联网应用的量级越来越大,单台计算机的能力有限,需要借…

RPC介绍与原理

RPC介绍与原理 RPC介绍 RPC是什么 RPC(Remote Procedure Call)是一种进程间通信方式。简单地说就是能使应用像调用本地方法一样的调用远程的过程或服务,可以应用在分布式服务、分布式计算、远程服务调用等许多场景。说起 RPC 大家并不陌生…

RPC理论原理

目录 集群和分布式RPC通信原理 集群和分布式 集群:每一台服务器独立运行一个工程的所有模块。 分布式:一个工程拆分了很多模块,每一个模块独立部署运行在一个服务器主机上,所有服务器协同工作共同提供服务,每一台服务…

一文搞懂RPC原理

了解RPC: RPC(Remote Procedure Call Protocol)——远程过程调用协议: 一般用于实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地资源一样,通过网络传输去访问远端系统资源。 描述&…

rpc简介及原理

1.RPC简介及原理介绍 RPC技术内部原理是通过两种技术的组合来实现的:本地方法调用 和 网络通信技术。 1.1 RPC简介 在上述本地过程调用的例子中,我们是在一台计算机上执行了计算机上的程序,完成调用。随着计算机技术的发展和需求场景的变化…

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服务器系统,您将需要以下内容:…