Ring的数据结构
- 设备表(devs):将所有Device编号,设备表中的每一项对应一个Device,其中记录了Device的具体位置信息,包括Device Name, Device ID, IP, Port, Weight, Region和Zone等信息
-
移位值(part_shift):表示Hash之后得出的Key进行移位的位数
e.g. 假设有65536个node(2^16),有128(2^7)倍的partition数(2^23)。由于MD5是32位,使用part_shift(= 32 - partition_power)将Key映射到partition的2^23的一致性Hash环中
-
设备查询表(replica2part2dev_id):存储Partition的各个副本与具体的Device ID的映射信息。replica2part2dev_id的每一列对应一个Partition,每一行对应Partition的一个副本
Ring具体映射过程
- 使用对象的层次结构account/container/object计算出的Key作移位运算后得出Partition的编号,然后在replica2part2dev_id表中查得对应的三备份的Device ID,最后到devs中查得对应Device ID的IP及端口号
构建Ring
Swift使用swift-ring-builder工具构建一个Ring,可分为三个步骤:
- 创建Ring文件
swift-ring-builder <build_file> create <part_power> <replicas> <min_part_hours>
build_file是build的文件名,2^part_power是Partition的个数,replicas是副本的个数,min_part_hours单位为小时,一般设置24小时,表示某个Partition在移动后必须等待指定的时间才能再次移动 - 添加设备到Ring中
swift-ring-builder <build_file> add [r<region>] z<zone>-<ip>:<port>/<device_name>_<meta> <weight>
region和zone表示Region和Zone的编号,ip:port表示设备所在节点的IP地址及端口号,device_name是该设备在该节点的名次(如sdb1),meta是该设备的元数据,weight是该设备的权重 - 分配Partition
swift-ring-builder <build_file> rebalance
rebalance操作会根据builder file的定义将Partition分配到不同的设备。在rebalance之后需要将生成的Ring文件复制到所有运行相应服务(Account, Container或Object)的节点上,然后用该Ring文件作为参数启动相应的服务
源码调用过程:
swift-ring-builder工具的源码入口是bin/swift-ring-builder,这个脚本仅仅是对swift.cli.ringbuilder模块的封装,直接调用了swift.cli.ringbuilder的main函数
- 首先调用swift.cli.ringbuilder.Commands类的create()函数完成builder文件的构建
- 其次调用swift.cli.ringbuilder.Commands类的add()函数完成设备的添加并保存到builder文件中
- 最后调用swift.cli.ringbuilder.Commands类的rebalance()函数实现balance并最终生成ring(.gz)文件
builder的数据结构:
ring(.gz)文件的数据结构上述已分析,builder的数据结构与ring的数据结构稍有差异,包括:
{'part_power': self.part_power,
'replicas': self.replicas,
'min_part_hours': self.min_part_hours,
'parts': self.parts,
'devs': self.devs,
'devs_changed': self.devs_changed,
'version': self.version,
'overload': self.overload,
'_replica2part2dev': self._replica2part2dev,
'_last_part_moves_epoch': self._last_part_moves_epoch,
'_last_part_moves': self._last_part_moves,
'_last_part_gather_start': self._last_part_gather_start,
'_dispersion_graph': self._dispersion_graph,
'dispersion': self.dispersion,
'_remove_devs': self._remove_devs}
builder(.builder)文件和ring(.gz)文件的持久化方式
builder(.builder)文件的持久化方式
- 在调用swift.cli.ringbuilder.Commands类的create()函数内部,通过调用的是save()函数将Ring的初始化信息保存到builder(.builder)文件中去
- save()函数内部首先将Ring的初始化信息转化成builder数据结构构成的dict对象,然后采用pickle序列化的方式将dict对象持久化到builder(.builder)文件中,采用的是更高效的新的二进制协议
ring(.gz)文件的持久化方式
- 调用swift.common.ring.Ring类的save()函数实现ring(.gz)文件的持久化
- save()函数内部首先将ring的数据结构转化成得对应的dict对象,再将其转化成JSON形式,然后将这些数据封装成字符串后写入到Gzip压缩文件中,最后flash