转自:https://www.ustack.com/blog/cephxuliehua/

作为主要和磁盘、网络打交道的分布式存储系统,序列化是最基础的功能之一,今天我们来看一下Ceph中序列化的设计与实现。
 
 
1 Ceph序列化的方式
序列化(ceph称之为encode)的目的是将数据结构表示为二进制流的方式,以便通过网络传输或保存在磁盘等存储介质上,其逆过程称之为反序列化(ceph称之为decode)。 例如对于字符串“abc”,其序列化结果为8个字节(bytes):
03 00 00 00 61 62 63
其中头四个字节(03 00 00 00)表示字符串的长度为3个字符,后3个字节(61 62 63)分别是字符“abc”的ASCII码的16进制表示。 Ceph采用little-endian的序列化方式,即低地址存放最低有效字节,所以32位整数0x12345678的序列化结果为78 56 34 12。
 
由于序列化在整个系统中是非常基本,非常常用的功能,Ceph将其序列化方式设计为一个同一的结构,即任意支持序列化的数据结构,都必须提供一对定义在全局命名空间上的序列化/反序列化(encode/decode)函数。例如,如果我们定义了一个结构体inode,就必须在全局命名空间中定义以下两个方法:
encode(struct inode, bufferlist bl);
decode(struct inode, bufferlist::iterator bl);
 
在此基础上,序列化的使用就变得非常容易 。 即对于任意可序列化的类型T的实例instance_T,都可以通过以下语句:
::encode(instance_T, instance_bufferlist);
将instance_T序列化并保存到bufferlist类的实例instance_bufferlist中。
 
以下代码演示了将一个时间戳以及一个inode序列化到一个bufferlist中。
utime_t timestamp;
inode_t inode;
bufferlist bl;
 
::encode(timetamp, bl)
::encode(inode, bl);
 
bufferlist类(定义于include/buffer.h)是ceph核心的缓存类,用于保存序列化结果、数据缓存、网络通讯等,可以将bufferlist理解为一个可变长度的char数组。关于bufferlist的设计与实现,可以参考《Ceph中Bufferlist》。
 
序列化后的数据可以通过反序列化方法读取,例如以下代码片段从一个bufferlist中反序列化一个时间戳和一个inode(前提是该bl中已经被序列化了一个utime_t和一个inode,否则会报错)。
bufferlist::iterator bl;
 
::decode(timetamp, bl)
::decode(inode, bl);
 
2 数据结构的序列化
Ceph为其所有用到数据类型提供了序列化方法或反序列化方法,这些数据类型包括了绝大部分基础数据类型(int、bool等)、结构体类型的序列化(ceph_mds_request_head等)、集合类型(vector、list、set、map等)、以及自定义的复杂数据类型(例如表示inode的inode_t等),以下分别介绍不同数据类型的序列化实现方式。
 
2.1 基本数据类型的序列化
基本数据类型的序列化结果基本就是该类型在内存中的表示形式。基本数据类型的序列化方法使用手工编写,定义在include/encoding.h中,包括以下类型:
  • __u8, __s8, char, bool
  • ceph_le64, ceph_le32, ceph_le16,
  • float, double,
  • uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t,
  • string, char*
在手工编写encode方法过程中,为了避免重复代码,借助了WRITE_RAW_ENCODER和WRITE_INTTYPE_ENCODER两个宏。
 
2.2 结构体类型的序列化
结构体类型的序列化方法与基本数据类型的序列化方法一致,即使用结构体的内存布局作为序列化的形式。在结构体定义完成后,通过调用WRITE_RAW_ENCODER宏函数生成结构体的全局encode方法,例如结构体ceph_mds_request_head相关结构实现如下。
 
struct ceph_mds_request_head {
 __le64 oldest_client_tid;
 __le32 mdsmap_epoch;
 __le32 flags;
 __u8 num_retry, num_fwd;
 __le16 num_releases;
 __le32 op;
 __le32 caller_uid, caller_gid;
 __le64 ino;
} __attribute__ ((packed));
WRITE_RAW_ENCODER(ceph_mds_request_head)
 
其中:
  • ceph_mds_request_head结构体定义在include/ceph_fs.h
  • WRITE_RAW_ENCODER(ceph_mds_request_head)语句位于include/types.h
  • WRITE_RAW_ENCODER宏函数定义在include/encoding.h
 
WRITE_RAW_ENCODER宏函数实际上是通过调用encode_raw实现的,而encode_raw调用bufferlist的append的方法,通过内存拷贝,将数据结构放入到bufferlist中。相关代码为:
template<class T>
inline void encode_raw(const T& t, bufferlist& bl)
{
  bl.append((char*)&t, sizeof(t));
}
template<class T>
inline void decode_raw(T& t, bufferlist::iterator &p)
{
  p.copy(sizeof(t), (char*)&t);
}
 
2.3 集合数据类型的序列化
集合数据类型序列化的基本思路包括两步:
  1. 序列化集合大小,
  2. 序列化集合内的所有元素
 
例如vector<T>& v的序列化方法:
template<class T>
inline void encode(const std::vector<T>& v, bufferlist& bl)
{
  __u32 n = v.size();
  encode(n, bl);
  for (typename std::vector<T>::const_iterator p = v.begin(); p != v.end(); ++p)
    encode(*p, bl);
}
其中元素的序列化通过调用该元素的encode方法实现。
 
常用集合数据类型的序列化已经由Ceph实现,位于include/encoding.h中,包括以下集合类型:
  • pair, triple
  • list, set, vector, map, multimap
  • hash_map, hash_set
  • deque
集合类型的序列化方法皆为基于泛型(模板类)的实现方式,适用于所有泛型派生类。
 
2.4 复杂数据类型的序列化
除以上两种业务无关的数据类型外,其它数据类型的序列化实现包括两部分:
  1. 在类型内部现实encode方法,
  2. 将类型内部的encode方法重定义为全局方法。
 
以下以utime_t类为例:
 
class utime_t {
 struct {
  __u32 tv_sec, tv_nsec;
 } tv;
 
 void encode(bufferlist &bl) const {
  ::encode(tv.tv_sec, bl);
  ::encode(tv.tv_nsec, bl);
 }
 void decode(bufferlist::iterator &p) {
  ::decode(tv.tv_sec, p);
  ::decode(tv.tv_nsec, p);
 }
};
WRITE_CLASS_ENCODER(utime_t)
 
utime_t内部实现了encode和decode两个方法,WRITE_CLASS_ENCODER宏函数将这两个方法转化为全局方法。
 
WRITE_CLASS_ENCODER宏函数定义于include/encoding.h中,其定义如下:
 
#define WRITE_CLASS_ENCODER(cl)
  inline void encode(const cl &c, bufferlist &bl, uint64_t features=0) {
    ENCODE_DUMP_PRE(); c.encode(bl); ENCODE_DUMP_POST(cl); }
  inline void decode(cl &c, bufferlist::iterator &p) { c.decode(p); }
 
复杂数据结构内部的encode方法的实现方式通常是调用其内部主要数据结构的encode方法,例如utime_t类的encode方法实际上是序列化内部的tv.tv_sec和tv.tv_nsec两个成员。
 
以下附件是5月10日在车库咖啡举办的《奔跑吧!OpenStack》资料,分享给需要的朋友们!
 
 

Ceph中的序列化的更多相关文章

  1. .NET中XML序列化的总结

    [题外话] 以前虽然常用.NET中的序列化,但是常用的BinaryFormatter,也就是二进制文件的序列化,却鲜用XML的序列化.对于XML序列化,.NET中同样提供了一个非常方便的工具XmlSe ...

  2. C#中JSON序列化和反序列化

    有一段时间没有到博客园写技术博客了,不过每天逛逛博客园中大牛的博客还是有的,学无止境…… 最近在写些调用他人接口的程序,用到了大量的JSON.XML序列化和反序列化,今天就来总结下json的序列化和反 ...

  3. Net中JSON序列化和反序列化处理(日期时间特殊处理)

    0  缘由 笔者最近在web api端使用Json.Net进行序列化处理,而在调用端使用DataContractSerializer进行反序列化,遇到日期时间处理反序列化不成功[备注:笔者使用Net ...

  4. .net中对象序列化技术浅谈

    .net中对象序列化技术浅谈 2009-03-11 阅读2756评论2 序列化是将对象状态转换为可保持或传输的格式的过程.与序列化相对的是反序列化,它将流转换为对象.这两个过程结合起来,可以轻松地存储 ...

  5. JavaScriptSerializer中日期序列化问题解决方案

    JavaScriptSerializer中日期序列化问题解决方案 直接进入主题: class Student { public int age { get; set; } public DateTim ...

  6. 1 weekend110的复习 + hadoop中的序列化机制 + 流量求和mr程序开发

    以上是,weekend110的yarn的job提交流程源码分析的复习总结 下面呢,来讲weekend110的hadoop中的序列化机制 1363157985066      13726230503  ...

  7. spring mvc3中JACKSON序列化日期格式的问题 - 墙头草的Java - BlogJava

    body { font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI ...

  8. AFN中请求序列化的设置

    最近遇到一个需求:要求从客户端传到服务器的参数是json字符串,于是我本能的用pod装了afn然后进行了request和response Serialization的相关设置 AFHTTPSessio ...

  9. 在Java中进行序列化和反序列化

    对象序列化的目标是将对象保存在磁盘中,或者允许在网络中直接传输对象. 对象序列化允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久保存在磁盘上或者通过网络将这种二进制流传输 ...

随机推荐

  1. 正确使用索引(sql优化),limit分页优化,执行计划,慢日志查询

    查看表相关命令 - 查看表结构   desc 表名- 查看生成表的SQL   show create table 表名- 查看索引   show index from  表名 使用索引和不使用索引 由 ...

  2. “格式化HDFS后,HMaster进程启动失败”的问题解决

    用 hadoop namenode -fromat 格式化后,用./start-hbase.sh 启动HMaster和HRegionServer,但是过几秒种后HMaster进程自动关闭,HRegio ...

  3. hbase中清空整张表的数据

    hbase(main):005:0> truncate 'fr:test' Truncating 'FaceBase' table (it may take a while): - Disabl ...

  4. 前端基础之css样式(选择器)

    一.css概述 CSS是Cascading Style Sheets的简称,中文称为层叠样式表,对html标签的渲染和布局 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. 例如 二.c ...

  5. Vue:实践学习笔记(3)——组件使用

    Vue:实践学习笔记(3)——组件使用 全局注册 1.注册组件 Vue.component('my-component',{ //选项 }) 说明:my-component就是注册的组件自定义的标签名 ...

  6. 深入浅出Node.js(下)

    (五):Node.js的异步实现 专栏的第五篇文章<Node.js的异步实现>.之前介绍了Node.js的事件机制,也许读者对此尚会觉得意犹未尽,因为仅仅只是简单的事件机制,并不能道尽No ...

  7. 前端之HTML基础

    一.初识HTML 1.web服务的本质 方式一:服务端 import socket def main(): sock = socket.socket(socket.AF_INET, socket.SO ...

  8. Shell编程之case条件

    一.case条件语句 1.语法 case "变量" in 值 1) 指令 1... ;; 值 2) 指令 2... ;; *) 指令 3... esac case条件语句的执行流程 ...

  9. SOA、SOAP、REST、RPC

    1.SOA SOA(面向服务的软件架构.Service Oriented Architecture),是一种软件设计模式,主要应用于不同应用组件之间通过某种协议来互操作.例如典型的  通信网络协议.因 ...

  10. java常用日期操作方法

    package com.wujiangpo.test.util; import java.text.ParseException; import java.text.SimpleDateFormat; ...