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


假设你是客户端,现在要调用远程的加法计算服务,你与服务端商定好了发送数据的格式:发送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. Struts2学习笔记(六)——Action处理请求参数

    在struts2框架中关于Action处理请求参数有两种方案(三个方式),表单属性的名称应该和在Action类中定义的成员属性或者在JavaBean中定义的成员属性名称一样: 1.属性驱动 1)直接在 ...

  2. 【Ubuntu16】apt-get安装MariaDB

    一.Mysql背景信息 Mysql在互联网早期就流行了,追求速度.简单.坚持开源.几乎支持所有操作系统.完全支持多用户.多线程,支持海量数据存储,采用MyISAM.InnoDB两大存储引擎优势互补.但 ...

  3. JDK环境变量的配置

    JDK环境变量的配置.. ==================>>> 右键“我的电脑”,选择“属性”,弹出的对话框中选择“高级系统设置” 在弹出的系统属性对话框中.选择“高级”,再选 ...

  4. WeQuant交易策略—RSI

    RSI指标策略 策略介绍 RSI(相对强弱指标),是通过一段时期内的平均收盘上涨和下跌数,计算价格上涨所产生的波动占整个波动的百分比,来分析市场买卖盘的意向和实力. 计算公式(以日为单位举例) RSI ...

  5. Maven详解(六)------ 生命周期

    1.什么是 生命周期? Maven 强大的原因是有一个十分完善的生命周期,生命周期可以理解为项目构建步骤的集合,它定义了各个构建环节的执行顺序,有了这个顺序,Maven 就可以自动化的执行构建命令. ...

  6. 零基础到精通Web渗透测试的学习路线

    小编相信很多新手都会遇到以下几个问题 1.零基础想学渗透怎么入手? 2.学习web渗透需要从哪里开始? 这让很多同学都处于迷茫状态而迟迟不下手,小编就在此贴给大家说一下web渗透的学习路线,希望对大家 ...

  7. spring boot / cloud (八) 使用RestTemplate来构建远程调用服务

    spring boot / cloud (八) 使用RestTemplate来构建远程调用服务 前言 上周因家里突发急事,请假一周,故博客没有正常更新 RestTemplate介绍: RestTemp ...

  8. 【Java学习笔记之三十二】浅谈Java中throw与throws的用法及异常抛出处理机制剖析

    异常处理机制 异常处理是对可能出现的异常进行处理,以防止程序遇到异常时被卡死,处于一直等待,或死循环. 异常有两个过程,一个是抛出异常:一个是捕捉异常. 抛出异常 抛出异常有三种形式,一是throw, ...

  9. Windows下mysql忘记root密码

    1. 首先检查mysql服务是否启动,若已启动则先将其停止服务,可在开始菜单的运行,使用命令: net stop mysql 打开第一个cmd窗口,切换到mysql的bin目录,运行命令: mysql ...

  10. postman 第3节 API请求和查看响应结果(转)

    请求 postman支持很多请求类型,界面左侧可以看到请求类型:get.post.put.patch等,右侧是发送和保存按钮,下方是请求支持的认证方式.信息头.信息体.私有脚本和测试结果.下面我们介绍 ...