RPC框架实现 - 通信协议篇
RPC(Remote Procedure Call,远程过程调用)框架是分布式服务的基石,实现RPC框架需要考虑方方面面。其对业务隐藏了底层通信过程(TCP/UDP、打包/解包、序列化/反序列化),使上层专注于功能实现;框架层面,提供各类可选架构(多进程/多线程/协程);应对设备故障(高负载/死机)、网络故障(拥塞/网络分化),提供相应容灾措施。
RPC节点间为了协同工作、实现信息交换,需要协商一定的规则和约定,例如字节序、压缩或加密算法、各字段类型。通信协议的应用随处可见,例如我们对可选信息或字段经常使用TLV进行编码,HTTP、FTP等协议基于可读文本的 "Field: Value" 格式,各种系统也经常使用json、XML格式完成相互间通信。
不同的通信协议适用于不同的应用场景,比如内部系统的交互我们选择json,一来可读性较好,二来各种语言都提供了解析json的库、方便编码。Google Protocol Buffers是生成环境中常用的通信协议,除了可以设定Client/Server间通信格式,Protocol Buffers还对数据进行压缩,节省传输流量、加快传输速度。下面我们来了解Google Protocol Buffers。
Protocol Buffers
我们看如何使用Protocol Buffers(以下简称PB),首先在.proto文件中定义数据格式,下面以Person.proto为例:
message Person {
required string name = ;
required int32 id = ;
optional string email = ;
enum PhoneType {
MOBILE = ;
HOME = ;
WORK = ;
}
message PhoneNumber {
required string number = ;
optional PhoneType type = [default = HOME];
}
repeated PhoneNumber phone = ;
}
message类型内,可以定义int、string、bool、string等类型的字段,也可以嵌套定义messages类型。每个字段可以是required、optional或repeated类型,分别表示必须每次通信必须填充该字段、可选或可重复。每个message类型内的每个字段被赋值唯一的数字值,PB以二进制格式进行数据传输,数字值在二进制中作为该字段的标识。关于PB数据格式的更多内容可参考Protocol Buffers Language Guide。
完成数据定义后,接下来可以使用protoc工具解析Person.proto文件,生成Person类:
protoc --cpp_out=/home/bangerlee/PB ./Person.proto
执行以上命令后,可以看到 /home/bangerlee/PB 目录下生成了两个文件:
person.pb.cc person.pb.h
其中定义了操作(get/set)Person类各个字段的函数。
有了接口,我们就可以在代码中这样使用Person类,写入操作如下:
Person person;
person.set_name("bangerlee");
person.set_id();
person.set_email("bangerlee@gmail.com");
fstream output("myfile", ios::out | ios::binary);
person.SerializeToOstream(&output);
读取操作如下:
fstream input("myfile", ios::in | ios::binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;
以上我们初步了解了如何使用PB,PB运用了一些编码规则,使得需要传输的数据(二进制格式)更小,下面我们就来了解PB如何对不同数据类型的编码规则。
编码(Encoding)
对整形int、字符串类型string等,PB有不同的编码方式。对整型int,PB使用了Varints编码方式,Varints编码的优势是使用了更少的bytes来表示很小的int类型值。
Varints编码方式中,每个byte的最高位bit有特殊含义,如果为1,表示后续的byte也是这个数字的一部分;如果为0,则表示结束。剩余的7个bit用于表示数据。数字300用Varints编码方式表示为:
由Varints编码规则,去掉第一个byte的最高位1,去掉第二个byte的最高位0,则有:
→
Varints字节序使用little-endian,以上数字用big-endian并转换成10进制有:
→ ++
→
→ + + + =
以上了解了Varints对int整型的编码方式,我们再来看PB如何编码更多数据类型:

PB编码中,数据以key-value的形式表示,第一个byte即为key。以上表格中不同数据类型对应指定type值,假设message中各字段的数字标识为tag,则key、type和tag有以下对应关系:
key = tag << | type
即key的最后3个bit用于存储type,有了这层关系,我们试着演算PB中对int和string的编码。
假设我们截获到以下PB数据:
这段数据具体表示什么?我们用以上对应关系演算一下,首先该数据key是08,二进制表示即:
最后3个bit表示type,即type为0(Varint格式数据),左移3位得到tag值为1。有了这些信息,我们可以知道这个数据应该是这样定义的:
message Test1 {
xxx int32 a = ;
}
继续地,我们用Varint格式来解析 96 01,有以下演算过程:
=
→ ++ (丢弃最高位的bit并转为big-endian)
→
→ + + + =
因此我们可以知道这段数据表示150这个数。
又假设我们截获到以下一段PB编码:
6e
同样套用以上关系,key是12,二进制表示即:
最后3个bit表示type,即type为2(Length-delimited),左移3位得到tag值为2。有了这些信息,我们知道这个数据可能是这样定义的:
message Test2 {
xxx string b = ;
}
数据类型具体是string、bytes或其他,这并不影响我们解析这段数据,对于Length-delimited格式数据,第2个byte表示数据长度(Len),对应以上编码即Len为7,这实质是TLV编码格式。
后续的7个bytes表示有效的传输数据,为UTF-8编码下的"testing"字符串。
小结
以上介绍了通信协议 - Google Protocol Buffers,了解了其基本使用方法和编码方式。PB支持前向兼容,可以在不修改Client/Server程序的情况下修改其中一端的数据格式,在各种RPC框架中经常可以看到它的身影。
Reference: Protocol Buffers Developer Guide
RPC框架实现 - 通信协议篇的更多相关文章
- RPC框架实现
转载RPC框架实现 RPC(Remote Procedure Call,远程过程调用)框架是分布式服务的基石,实现RPC框架需要考虑方方面面.其对业务隐藏了底层通信过程(TCP/UDP.打包/解包.序 ...
- 基于Netty和SpringBoot实现一个轻量级RPC框架-协议篇
基于Netty和SpringBoot实现一个轻量级RPC框架-协议篇 前提 最近对网络编程方面比较有兴趣,在微服务实践上也用到了相对主流的RPC框架如Spring Cloud Gateway底层也切换 ...
- 一个简单RPC框架是怎样炼成的(I)——开局篇
开场白,这是一个关于RPC的相关概念的普及篇系列,主要是通过一步步的调整,提炼出一个相对完整的RPC框架. RPC(Remote Procedure Call Protocol)--远程过程调用协议, ...
- 基于Netty和SpringBoot实现一个轻量级RPC框架-Server篇
前提 前置文章: Github Page:<基于Netty和SpringBoot实现一个轻量级RPC框架-协议篇> Coding Page:<基于Netty和SpringBoot实现 ...
- 基于Netty和SpringBoot实现一个轻量级RPC框架-Client篇
前提 前置文章: <基于Netty和SpringBoot实现一个轻量级RPC框架-协议篇> <基于Netty和SpringBoot实现一个轻量级RPC框架-Server篇> 前 ...
- Hadoop系列番外篇之一文搞懂Hadoop RPC框架及细节实现
@ 目录 Hadoop RPC 框架解析 1.Hadoop RPC框架概述 1.1 RPC框架特点 1.2 Hadoop RPC框架 2.Java基础知识回顾 2.1 Java反射机制与动态代理 2. ...
- 看了这篇你就会手写RPC框架了
一.学习本文你能学到什么? RPC的概念及运作流程 RPC协议及RPC框架的概念 Netty的基本使用 Java序列化及反序列化技术 Zookeeper的基本使用(注册中心) 自定义注解实现特殊业务逻 ...
- 带你手写基于 Spring 的可插拔式 RPC 框架(一)介绍
概述 首先这篇文章是要带大家来实现一个框架,听到框架大家可能会觉得非常高大上,其实这和我们平时写业务员代码没什么区别,但是框架是要给别人使用的,所以我们要换位思考,怎么才能让别人用着舒服,怎么样才能让 ...
- RPC框架调用过程详解
RPC框架调用过程详解 2017年09月16日 21:14:08 荷叶清泉 阅读数 6275 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. ...
随机推荐
- Fis3的前端模块化之路[基础篇]
Fis3版本:v3.4.22 fis3是一个构建工具 解决前端开发中自动化工具.性能优化.模块化框架.开发规范.代码部署.开发流程等问题. 安装 npm install -g fis3 运行 fis3 ...
- C语言 · Anagrams问题
问题描述 Anagrams指的是具有如下特性的两个单词:在这两个单词当中,每一个英文字母(不区分大小写)所出现的次数都是相同的.例如,"Unclear"和"Nuclear ...
- ExtJS 4.2 Grid组件的单元格合并
ExtJS 4.2 Grid组件本身并没有提供单元格合并功能,需要自己实现这个功能. 目录 1. 原理 2. 多列合并 3. 代码与在线演示 1. 原理 1.1 HTML代码分析 首先创建一个Grid ...
- Java学习之反射机制及应用场景
前言: 最近公司正在进行业务组件化进程,其中的路由实现用到了Java的反射机制,既然用到了就想着好好学习总结一下,其实无论是之前的EventBus 2.x版本还是Retrofit.早期的View注解框 ...
- 通过三次优化,我将gif加载优化了16.9%
WeTest 导读 现在app越来越炫,动不动就搞点动画,复杂的动画用原生实现起来挺复杂,如是就搞起gif播放动画的形式,节省开发成本. 背 景 设计同学准备给一个png序列,开发读取png序列, ...
- 解读发布:.NET Core RC2 and .NET Core SDK Preview 1
先看一下 .NET Core(包含 ASP.NET Core)的路线图: Beta6: 2015年7月27日 Beta7: 2015年9月2日 Beta8: 2015年10月15日 RC1: 2015 ...
- MongoDB集群配置
本文演示:(一个主服务器,一个备份服务器,三个仲裁服务器) 官方推荐副本集的成员数量为奇数,最多12个副本集节点,最多7个节点参与选举. 本文演示基于本机,用端口区分服务(每个服务器下新建db文件夹用 ...
- 让 asp.net 在 mac 上飞
.NET 不跨平台一直饱受争议,虽然微软前端时间放出些消息,要支持.NET跨平台的发展,但是微软一直坚持着不主动.不拒绝.不负责的三不态度,仍然用一种软件帝国的心态,折腾着一些毫无新意的东西.微软想要 ...
- 浅谈WebService的版本兼容性设计
在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...
- 微信小程序首次官方分享的纪要
先交代备注: 这次有关小程序的分享只有技术的 QA环节,其他如产品.入口.流量.与公众号的整合等等,回答都是暂时无法给出答案或不确定: 小程序最终发布时间官方也还未确定,不过说应该就是近期: 小程序的 ...