总结一下,理解算法时的几个问题,搞懂这些基本上就算理解Swift rebalnce的算法了。
例如有4台dev(编号 0,1, 2, 3)2的18次幂262144虚节点,我们都知道builder和ring中存储了一个数据结构_replica2part2dev,它表明了partition到replica到dev的映射关系,即某个文件的某个副本存储到哪个dev上,如下图
系统要做的是根据策略来生成随机的分布,让一个文件的三个副本可以存储到3个不同的dev上去。
其实builder文件里存储了一个字典型的数据结构,其中包含了整个系统所有的重要的数据。
'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, '_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, '_remove_devs': self._remove_devs
ring文件只存储记录part分布的数据
def serialize_v1(self, file_obj): # Write out new-style serialization magic and version: file_obj.write(struct.pack('!4sH', 'R1NG', 1)) ring = self.to_dict() json_text = json.dumps( {'devs': ring['devs'], 'part_shift': ring['part_shift'], 'replica_count': len(ring['replica2part2dev_id'])}) json_len = len(json_text) file_obj.write(struct.pack('!I', json_len)) file_obj.write(json_text) for part2dev_id in ring['replica2part2dev_id']: file_obj.write(part2dev_id.tostring())
一个上传文件的请求,proxy会来决定哪个node(dev)来存储这个文件和它的副本。然后向选好的dev发送请求。
例如 admin账户,向test容器中,上传一个名字为update.c的文件。会根据/admin/test/update.c + 设置的HASH_PATH_SUFFIX 生成一个md5 的值 然后解析成一个数字 例如35564 这个就是part,然后在_replica2part2dev 中查找它的三个replica所在的dev ,然后向这三个dev发送请求。
def get_nodes(self, account, container=None, obj=None): key = hash_path(account, container, obj, raw_digest=True) #md5(/account/contianer/obj + HASH_PATH_SUFFIX).digest() if time() > self._rtime: self._reload() part = struct.unpack_from('>I', key)[0] >> self._part_shift seen_ids = set() return part, [self._devs[r[part]] for r in self._replica2part2dev_id if not (r[part] in seen_ids or seen_ids.add(r[part]))]
dev对应的sort_key影响它分配的方式,系统生成一个字符串,它分成三段,进行排序,第一段为dev_want,第二段随机数,第三段dev_id,系统根据这个三段排序,然后查找最想want的dev
def _sort_key_for(self, dev): # The maximum value of self.parts is 2^32, which is 9 hex # digits wide (0x100000000). Using a width of 16 here gives us # plenty of breathing room; you'd need more than 2^28 replicas # to overflow it. # Since the sort key is a string and therefore an ascii sort applies, # the maximum_parts_wanted + parts_wanted is used so negative # parts_wanted end up sorted above positive parts_wanted. return '%016x.%04x.%04x' % ( (self.parts * self.replicas) + dev['parts_wanted'], randint(0, 0xffff), dev['id'])
parts_wanted = (sum_part/sum_weight )*dev_weith-dev_pars
系统设置的part减去已经分配的part 生成想要得到的part