栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

搜狗workflow——C++并行计算与异步网络引擎 序列化与反序列化 代码分析(一)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

搜狗workflow——C++并行计算与异步网络引擎 序列化与反序列化 代码分析(一)

2021SC@SDUSC

目录

一. 简单介绍序列化与反序列化

二.协议与序列化的关系

三.关于workflow的序列化接口

需要IDL的使用过程:

encode函数:

append函数

errno的设置


一. 简单介绍序列化与反序列化

   

       对象的序列化(Serialization),即把一个对象以流的方式,写入到文件中保存,叫写对象,也叫对象序列化,对象中包含的不仅仅是字符,使用字节流。

       对象的反序列化(deserailization),即把文件中保存的对象,以流的方式读取出来,叫做读取对象,也叫对象的反序列。读取的文件保存的都是字节,使用字节流。

对象序列化的作用:在传递,和保存对象(object)的时候,保证对象的完整性和可传递性。

       在翻看其它文章时发现底下有个评论很棒,概括得很全面:序列化是指把一个对象变成二进制内容,本质上就是一个byte[]数组。 为什么要把对象序列化呢?因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程,这样,就相当于把对象存储到文件或者通过网络传输出去了。 有序列化,就有反序列化,即把一个二进制内容(也就是byte[]数组)变回对象。有了反序列化,保存到文件中的byte[]数组又可以“变回对象,或者从网络上读取byte[]并把它“变回”对象。

 

二.协议与序列化的关系

关于协议:需要在发送请求的时候定一个边界,在请求收到的时候按照这个设定的边界进行数据分割,避免语义不一致的事情发生,而这个边界语义的表达,就是所谓的协议。

协议层是凌驾于序列化层之上的一层。协议层和序列化层之间还有传输层,动态代理层。

protocol 层主要用于配置 refer(发现服务) 和 exporter(暴露服务) 的实现方式,transport 层定义了传输的方式,codec 层诠释了具体传输过程中报文解析的方式,serialize 层负责将对象转换成字节,以用于传输,proxy 层负责将这些细节屏蔽。
它们的包含关系如下:protocol > transport > codec > serialize

 

 

用户自定义协议,需要提供协议的协议的序列化和反序列化方法。

三.关于workflow的序列化接口

实现序列化的方案有很多,如JSON/XML,众多的序列化方案中,按照存储方案,可分为字符串存储和二进制存储,字符串存储是可读的,但是由于以上问题,这里只考虑二进制存储。二进制存储中可分为需要IDL和不需要IDL,或分为自描述与非自描述(反序列化是否需要IDL)。

workflow采取的是IDL序列化:

需要IDL的使用过程
  • 使用该方案所定义的IDL语法编写schema
  • 使用该方案提供的编译器将schema编译成生产方和消费方所用语言的代码(类或者模块)
  • 数据生产方引用该代码,根据其接口,构建数据,并序列化
  • 消费方引用该代码,根据其接口读取数据

在workflow中, 在ProtocolMessage.h里,序列化反序列化接口如下:

namespace protocol
{

class ProtocolMessage : public CommMessageOut, public, CommMessageIn
{
private:
    virtual int encode(struct iovec vectors[], int max) = 0;//encode函数为序列化函数
    virtual int append(const char *buf, size_t size) = 0;//append函数为反序列化函数
    ...
};

}

在上述接口中定义了两个函数,其中encode函数为序列化函数,append函数为反序列化函数。

encode函数:
  • encode函数在消息被发送之前调用,每条消息只调用一次。
  • encode函数里,用户需要将消息序列化到一个vector数组,数组元素个数不超过max。目前max的值为8192。
  • 结构体struct iovec定义在请参考系统调用readv和writev。
  • encode函数正确情况下的返回值在0到max之间,表示消息使用了多少个vector。
    • 如果是UDP协议,请注意总长度不超过64k,并且使用不超过1024个vector(Linux一次writev只能1024个vector)。
      • UDP协议只能用于client,无法实现UDP server。
  • encode返回-1表示错误。返回-1时,需要置errno。如果返回值>max,将得到一个EOVERFLOW错误。错误都在callback里得到。
  • 为了性能考虑vector里的iov_base指计指向的内容不会被复制。所以一般指向消息类的成员。

扩展知识:IO流程中的io向量:iovec

为了提高从磁盘读取数据到内存的效率,引入了IO向量机制,IO向量即struct iovec,主要在API接口在readv和writev中使用。

struct iovec成为IO向量,其结构如下;

#include

struct iovec{

      void *iov_base;

      size_t iov_len;

}

很明显,iov_base作为一个指针,用于记录buffer地址(一般为用户态地址),iov_len表示buffer的大小。

在用户态或内核态中使用时,一般均采用指针的形式:struct iovec *iov。当然一般也给出其元素的个数count。

append函数
  • append函数在每次收到一个数据块时被调用。因此,每条消息可能会调用多次。
  • buf和size分别是收到的数据块内容和长度。用户需要把数据内容复制走。
    • 如果是UDP协议,一次append一定是一个完整的数据包。
  • append函数返回0表示消息还不完整,传输继续。返回1表示消息结束。-1表示错误,需要置errno。
  • 总之append的作用就是用于告诉框架消息是否已经传输结束。不要在append里做复杂的非必要的协议解析。

errno的设置
  • encode或append返回-1或其它负数都会被理解为失败,需要通过errno来传递错误原因。用户会在callback里得到这个错误。
  • 如果是系统调用或libc等库函数失败(比如malloc),libc会肯定会设置好errno,用户无需再设置。
  • 一些消息不合法的错误是比如常见的,比如可以用EBADMSG,EMSGSIZE分别表示消息内容错误,和消息太大。
  • 用户可以选择超过系统定义errno范围的值来表示一些自定义错误。一般大于256的值是可以用的。
  • 请不要使用负数errno。因为框架内部用了负数来代表SSL错误(证书出现错误)。

------------------------------------------------------------------------

参考文献:序列化和反序列化 - 知乎 (zhihu.com)

帅枫 (cnblogs.com)

(41条消息) 序列化和反序列化的详解_tree_ifconfig的博客-CSDN博客_序列化和反序列化

docs/tutorial-10-user_defined_protocol.md · 搜狗开源/workflow - Gitee.com

IO流程中IO向量iovec - YoungerChina - 博客园 (cnblogs.com)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/297204.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号