为什么需要序列化和反序列化?


假设你是客户端,现在要调用远程的加法计算服务,你与服务端商定好了发送数据的格式:发送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接口,必须实现三个函数:

  1. serialize函数,把request/response序列化到参数指定的数组中。
  2. deserialize函数,把参数指定的数组中的二进制字节流反序列化成request/response。
  3. 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-序列化与反序列化的设计与实现的更多相关文章

  1. 高性能的序列化与反序列化:kryo的简单使用

    前言:kryo是个高效的java序列化/反序列化库,目前Twitter.yahoo.Apache.strom等等在使用该技术,比如Apache的spark.hive等大数据领域用的较多. 为什么使用k ...

  2. DRF框架(三)——media资源路径设置、多表设计复习及补充、序列化组件(ModelSerializer)操作多表(序列化与反序列化)、多表序列化与反序列化整合(重点)

    media资源路径设置  (设置好后把图片放在这个文件夹中,通过链接能访问到图片) 1.先在根目录设置一个media文件夹 2.配置settings.py,加上下面的 MEDIA_URL = '/me ...

  3. C#对象序列化与反序列化zz

      C#对象序列化与反序列化(转载自:http://www.cnblogs.com/LiZhiW/p/3622365.html) 1. 对象序列化的介绍........................ ...

  4. C#对象序列化与反序列化

    C#对象序列化与反序列化(转载自:http://www.cnblogs.com/LiZhiW/p/3622365.html) 1. 对象序列化的介绍.......................... ...

  5. Asp.net中Json的序列化和反序列化(一)

    JSON是专门为浏览器中的网页上运行的JavaScript代码而设计的一种数据格式.在网站应用中使用JSON的场景越来越多,本文介绍ASP.NET中JSON的序列化和反序列化,主要对JSON的简单介绍 ...

  6. 序列化和反序列化的几种方式(JavaScriptSerializer 、XmlSerializer、DataContractSerializer)(一)

    JavaScriptSerializer 类 为启用 AJAX 的应用程序提供序列化和反序列化功能. 命名空间:   System.Web.Script.Serialization 程序集:  Sys ...

  7. ASP.NET中JSON的序列化和反序列化

    JSON是专门为浏览器中的网页上运行的JavaScript代码而设计的一种数据格式.在网站应用中使用JSON的场景越来越多,本文介绍 ASP.NET中JSON的序列化和反序列化,主要对JSON的简单介 ...

  8. java中对象的序列化和反序列化

    [对象的序列化和反序列化 ] 1.定义:序列化--将对象写到一个输出流中.反序列化则是从一个输入流中读取一个对象.类中的成员必须是可序列化的,而且要实现Serializable接口,这样的类的对象才能 ...

  9. lintcode : 二叉树的序列化和反序列化

    题目 二叉树的序列化和反序列化 设计一个算法,并编写代码来序列化和反序列化二叉树.将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”. 如何反序列化或序列化二叉树是没有限制 ...

随机推荐

  1. linux磁盘分区 简要

    基础知识: 以机械磁盘来说,磁盘的组成为盘片.机械手臂.磁头.主轴马达. 数据被写入在盘片上. 盘片分为 扇区Sector 柱面Cylinder 整块磁盘的第一个扇区特别重要 用来存放主引导分区Mas ...

  2. oracle中如何移动数据文件

    a.shutdown immediate关闭数据库  b.在os下拷贝数据文件到新的地点  c.Startup mount 启动到mount下  d.Alter database rename fil ...

  3. go web 第三天 学习笔记 --mysql

    CREATE TABLE `userinfo` ( `uid` INT() NOT NULL AUTO_INCREMENT, `username` VARCHAR() NULL DEFAULT NUL ...

  4. hdu 5040 Instrusive

    Instrusive Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Tota ...

  5. c语言中的文件格式化读写函数fscanf和fprintf函数

    很多时候我们需要写入数据到文件中时都觉得很困扰,因为格式乱七八槽的,可读性太差了,于是我们就想有没有什么函数可以格式化的从文件中输入和输出呢,还真有.下面我将讲解一下fscanf和fprintf的强大 ...

  6. ABP 框架学习-01篇

    从来没有自己写过太多的技术性文章,博客里面的文章都是拷贝别人的东西,做一个笔记功能给自己用的.最近觉得应该写点自己的学习博客 https://aspnetboilerplate.com/ ABP框架, ...

  7. 弹性布局flex

    前几天写过怪异盒子布局,以前在项目中用到弹性布局flex这个属性,当时没深入研究,这里各种查阅各种测试,把这个属性记录下 以免忘记, 弹性布局:是提供一种更加有效的方式来对一个容器中的条目进行排列.对 ...

  8. vue引入echarts、找不到的图表引入方法、图表中的点击事件

    1.在vue-cli项目中添加webpack配置,本文引入的最新版本.在 3.1.1 版本之前 ECharts 在 npm 上的 package 是非官方维护的,从 3.1.1 开始由官方 EFE 维 ...

  9. javascript 代码放在head和body的区别

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt239 1,在head中时,所代表的functions只加载而不执行,执行是在 ...

  10. 第2阶段——编写uboot之启动内核和制作Makefile(2)

    目标: 1   添加头文件setup.h和serial.h 2   写main函数   2.1 帮内核设置串口0, (内核启动会打印出启动信息) 2.2把内核读入到SDRAM 2.3设置参数(参考u- ...