转自: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》资料,分享给需要的朋友们!
 
 

最新文章

  1. Linux ftp 使用
  2. traceroute
  3. LR常见问题
  4. recurse_array_change_key_case()递规返回字符串键名全为小写或大写的数组
  5. poj 2409(polya定理模板)
  6. 不用预计算切向空间的Normal mapping
  7. struts2 JS获取上传文件的绝对路径,兼容IE和FF
  8. 计算BMI
  9. 工作整理: python报表系统常见错误整理
  10. 下拉框的change事件
  11. 腾讯云服务器配置node环境
  12. 在react-native项目中使用iconfont自定义图标库(android)
  13. 用织梦建站如何去掉a这个目录,还有内容页的a
  14. UE4 编译笔记
  15. A1054. The Dominant Color
  16. ubuntu 查看进程信息
  17. 深度学习课程笔记(十二) Matrix Capsule
  18. sql server 中进行除法运算时,如何得到结果是小数形式呢?
  19. PTA——四舍五入
  20. php 导出excel文件

热门文章

  1. 随机生成六位验证码函数版(python)
  2. excel同时冻结首行和首列怎么操作
  3. 201704 F-02创建财务凭证
  4. CoreThink主题开发(九)使用H-ui开发博客主题之用户个人主页
  5. 爬虫二 requests模块的使用
  6. Django设置上传文件夹
  7. Meta 数据中文显示
  8. smarty模板及其应用
  9. ERROR 2003 (HY000): Can&#39;t connect to MySQL server on &quot;&quot; (113)
  10. weblogic启动错误 Unrecognized option: -jrockit