SimpleRpc-序列化与反序列化的设计与实现
为什么需要序列化和反序列化?
假设你是客户端,现在要调用远程的加法计算服务,你与服务端商定好了发送数据的格式:发送8个字节的请求,前4字节是第一个数,后4字节是第二个数,服务端读取数据的时候也按照商定的方式读取。其实,这就是一个序列化和反序列化的过程。序列化:2个数字变成8个字节数据,反序列化:8个字节数据变成2个数字。但是这么做有个问题,那就是太容易出错,每次你还得考虑按照什么形式排列字段,每个字段几个字节,还要考虑大端小端等。
为了解决这个重复性并且容易出错的过程,我们有一个小小的改进:把常用数据类型的序列化和反序列化代码封装成基础库:
int readInt(char *, int size) //读一个整数
int writeInt(int, char *, int size) //写一个整数
double readDouble(char *, int size) //读一个double型数
int writeDouble(double, char *, int size) //写一个double型数
float readFloat(char *, int size) //读一个浮点数
int writeFloat(float, char *, int size) //写一个浮点数
string readString(char *, int size) //读一个字符串
int writeString(string, char *, int size) //写一个字符串
现在,我们可以序列化任何基础类型数据。但是有个问题来了:怎么序列化结构体咧?仔细想一下,结构体也是由最基本的数据类型组成的啊,我们可能会有下面的方案:
class SimpleRequest {
int a;
int b;
int serialize(char *buf, int size) {
writeInt(a, buf, size);
writeInt(b, buf + , size - );
return ;
}
int deserialize(char *buf, int size) {
a = readInt(buf, size);
b = readInt(buf + , size - );
return ;
}
};
但有些结构体中套用结构体,这种情况怎么处理呢?很好办,因为只要是结构体我们就已经实现了serialize和deserialize接口,只要调用这两个函数就可以。所以,最终的方案就是:对于基础数据类型,通过readXX和writeXX序列化,结构体类型通过serialize/deserialize序列化。
由于基础数据类型数目有限可枚举,并且结构体定义也有一定的语法,我们完全可以设计一个语法解析器,读取IDL定义的文件,自动生成序列化和反序列化的代码。大致流程如下:使用BNF范式来编写规则,用来描述我们自己定义的IDL(接口描述语言);然后使用JAVACC或者YACC根据编写的BNF范式生成解析IDL语言的代码,利用生成的代码解析我们用IDL定义的结构体文件,根据语法树查找其中的基础数据类型、用户自定义结构体,并进行有针对性的进行解析。Thrift和grpc的IDL解析都是这么做的,有兴趣的同学可以自己玩一下Javacc和yacc。
SimpleRpc的序列化与反序列化设计方案
SimpleRpc没有自己的序列化和反序列化具体实现方案,它要求用户自己实现这部分代码。我们的例子中使用的protobuf,protobuf在SimpleRpc并不是必须的,你可以换成任何一种序列化方式。SimpleRpc的设计方案如下图所示:

Request和Response是请求和响应的基类,继承自Serializable接口,必须实现三个函数:
- serialize函数,把request/response序列化到参数指定的数组中。
- deserialize函数,把参数指定的数组中的二进制字节流反序列化成request/response。
- bytes函数,得到结构体序列化成字节流的大小。
AddRequest和AddResponse是用户端必须实现的代码,我的例子中在这两个类里面嵌套了protobuf定义的request和response,当框架根据多态调用序列化和反序列化函数时,相应的类通过调用其成员protobuf实例的序列化和反序列化代码。由于框架所看到的结构都是Request或者是Response,隐藏其中的protobuf对框架而言是不可见的,你可以更换成任意一种序列化和反序列化方式。
小伙伴们可能有疑问,为什么AddRequest和AddResponse不直接继承自Serialzable,而是继承自中间的那层Request和Response,是不是多余了?是因为,Request和Response除了实现序列化和反序列化之外,还有其它接口需要实现,这里面为了只突出序列化相关而忽略了其它接口。
与其它RPC的设计方案对比
最早接触到的序列化是在Java的远程调用RMI中,但是Java的序列化太笨拙,它不仅序列化数据成员,还序列化其对象间引用关系,这导致其序列化后的字节数非常多,不是一种高效率的手段。接下来遇到的就是ICE以及Thrift中序列化,但是其序列化模块是和整个框架绑定到一起,为了只用一个序列化功能,你必须安装整个框架,还是有些笨拙。直到遇到了protobuf,它真正的把序列化从RPC框架中抽离出来,成为了现在使用最多的序列化框架。
我们的RPC和其它的RPC的不同点就在于,序列化和框架是分离的,你可以自由更换序列化方式,只要你实现了Request和Response接口(你甚至都可以自己针对特定的请求响应硬编码字节流),给用户更多的选择性。
SimpleRpc-序列化与反序列化的设计与实现的更多相关文章
- 高性能的序列化与反序列化:kryo的简单使用
前言:kryo是个高效的java序列化/反序列化库,目前Twitter.yahoo.Apache.strom等等在使用该技术,比如Apache的spark.hive等大数据领域用的较多. 为什么使用k ...
- DRF框架(三)——media资源路径设置、多表设计复习及补充、序列化组件(ModelSerializer)操作多表(序列化与反序列化)、多表序列化与反序列化整合(重点)
media资源路径设置 (设置好后把图片放在这个文件夹中,通过链接能访问到图片) 1.先在根目录设置一个media文件夹 2.配置settings.py,加上下面的 MEDIA_URL = '/me ...
- C#对象序列化与反序列化zz
C#对象序列化与反序列化(转载自:http://www.cnblogs.com/LiZhiW/p/3622365.html) 1. 对象序列化的介绍........................ ...
- C#对象序列化与反序列化
C#对象序列化与反序列化(转载自:http://www.cnblogs.com/LiZhiW/p/3622365.html) 1. 对象序列化的介绍.......................... ...
- Asp.net中Json的序列化和反序列化(一)
JSON是专门为浏览器中的网页上运行的JavaScript代码而设计的一种数据格式.在网站应用中使用JSON的场景越来越多,本文介绍ASP.NET中JSON的序列化和反序列化,主要对JSON的简单介绍 ...
- 序列化和反序列化的几种方式(JavaScriptSerializer 、XmlSerializer、DataContractSerializer)(一)
JavaScriptSerializer 类 为启用 AJAX 的应用程序提供序列化和反序列化功能. 命名空间: System.Web.Script.Serialization 程序集: Sys ...
- ASP.NET中JSON的序列化和反序列化
JSON是专门为浏览器中的网页上运行的JavaScript代码而设计的一种数据格式.在网站应用中使用JSON的场景越来越多,本文介绍 ASP.NET中JSON的序列化和反序列化,主要对JSON的简单介 ...
- java中对象的序列化和反序列化
[对象的序列化和反序列化 ] 1.定义:序列化--将对象写到一个输出流中.反序列化则是从一个输入流中读取一个对象.类中的成员必须是可序列化的,而且要实现Serializable接口,这样的类的对象才能 ...
- lintcode : 二叉树的序列化和反序列化
题目 二叉树的序列化和反序列化 设计一个算法,并编写代码来序列化和反序列化二叉树.将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”. 如何反序列化或序列化二叉树是没有限制 ...
随机推荐
- easyui 时间段校验,开始时间小于结束时间,并且时间间隔不能超过30天
//对easyui datetimebox的验证,开始时间要小于结束时间function validateDateTime(beginTimeId,endTimeId,whichTimeId){ co ...
- Redis入门 (CentOS7 + Redis-3.2.1)
1. 编译安装1.1 下载redis # cd /tmp/# wget http://download.redis.io/releases/redis-3.2.1.tar.gz# tar zxvf r ...
- 8.20.1 图形化:弹窗JOptionPane
最近在做swing程序中遇到使用消息提示框的,JOptionPane类其中封装了很多的方法. 很方便的,于是就简单的整理了一下. 1.1 showMessageDialog 显示一个带有OK 按钮的模 ...
- redis数据库操作的C++简单封装
用c++简单封装了redis的基本操作(hiredis) 接口包括:①链接和断开连接.②设置键值对(set).③查询键值对(get).④删除键值对(del).⑤将所有键显示出来 若任何一处发生错误,返 ...
- python基础教程(二)
继续第一篇的内容,讲解,python的一些基本的东西. 注释 为了让别人能够更容易理解程序,使用注释是非常有效的,即使是自己回头再看旧代码也是一样. >>> #获得用户名: > ...
- MVC客户端验证
引用JS 注意:删除Layout里面默认引用的JQUERY,否则可能引起JS冲突. <link href="~/Content/Site.css" rel="sty ...
- Centos 6 PXE安装
author:JevonWei 版权声明:原创作品 192.168.198.134作为安装服务器,由httpd服务共享安装程序 192.168.198.134作为dhcp服务器,客户机获取IP 一.安 ...
- h5video标签
在video标签中,我们可以使用属性:videoWidth & videoHeight,它获取的是video的宽度和高度(媒体本身).虽然不能直接使用,但是可以通过计算宽高比得到 video ...
- angularJS+Ionic移动端图片上传的解决办法
前端开发中经常会碰到图片上传的问题,网上的解决办法很多,可是有些图片上传的插件会有一些附属的插件,因此因为一个图片上传的问题可能额需要引入其他插件到项目中,久而久之项目会不伦不类,有时候插件之间也会有 ...
- 使用XmlWriter创建XML文件
using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Xml ...