本帖最后由 pig2 于 2014-11-21 15:13 编辑
问题导读
1.那个方法实现了从环ring中删除dev_id指定的device?
2.rebalance方法实现了什么功能?
上一篇
OpenStack Swift源码分析(5)----swift-ring-builder源代码解析之一
接续上一篇博文,继续解析文件swift-ring-builder。
6.来看方法remove_dev:
- def remove_dev(self, dev_id):
- """
- 从环ring中移除一个设备device;
- """
- # 根据dev_id获取指定的dev的id;
- dev = self.devs[dev_id]
- # 设置准备删除dev的weight的值为0;
- dev['weight'] = 0
- # 记录所删除的dev到列表中;
- self._remove_devs.append(dev)
- # _set_parts_wanted:方法根据dev的weight计算dev除了目前已经分配的partition数目而外,还要分配的partition数目;
- self._set_parts_wanted()
- self.devs_changed = True
- self.version += 1
复制代码
这个方法实现了从环ring中删除dev_id指定的device;
7.来看方法rebalance:
- def rebalance(self, seed=None):
- if seed:
- random.seed(seed)
-
- # 令实例中的ring为空
- self._ring = None
-
- # _last_part_moves_epoch:表示时间的偏移量;
- if self._last_part_moves_epoch is None:
- # 增加一些初始化设置的balance方法;
- self._initial_balance()
- # devs_changed:表明如果设备信息自上一次平衡后已经改变,则赋值为true;
- self.devs_changed = False
-
- # 分区数目:self.parts = 2 ** self.part_power;
- # get_balance:获取ring的balance的值;
- # balance的值具体解释如下:
- # 比如一个device至少需要123个partitions,而目前存在的是124个partitions,则balance的值计算如下:
- # (124-123)/123
- return self.parts, self.get_balance()
- retval = 0
-
- # 更新part moved时间;
- self._update_last_part_moves()
- last_balance = 0
-
- # _adjust_replica2part2dev_size:调整_replica2part2dev的大小,确保_replica2part2dev中的序列长度是正确的,相对于self.replicas当前的值;
- # 返回包含两个元素的元组:
- # 第一个元素是一个列表(partition,replicas)表示哪些副本需要(重新)分配给设备;
- # 第二个元素是一个计数器,表示多少副本需要被move;
- new_parts, removed_part_count = self._adjust_replica2part2dev_size()
- retval += removed_part_count
- self._reassign_parts(new_parts)
- retval += len(new_parts)
- while True:
- # 返回一个list(part,replica)对,需要重新分配;
- reassign_parts = self._gather_reassign_parts()
- # 重新分配的实际动作;
- self._reassign_parts(reassign_parts)
- retval += len(reassign_parts)
-
- while self._remove_devs:
- # 删除相应的dev;
- self.devs[self._remove_devs.pop()['id']] = None
- # 获取新的平衡比;
- balance = self.get_balance()
- if balance < 1 or abs(last_balance - balance) < 1 or retval == self.parts:
- break
- last_balance = balance
- self.devs_changed = False
- self.version += 1
- return retval, balance
复制代码
这个方法实现了执行重新平衡ring操作;
这个方法是builder中重要的方法,因为它会对ring上的devices执行分配和重新分配的分区的操作,基于权重、zones和最近执行的重新分配信息等;
程序会根据_last_part_moves_epoch是否为None来决定,程序执行的路线;如果为None(说明是第一次rebalance),程序会调用_initial_balance()方法,然后返回结果;其实它的操作跟_last_part_moves_epoch不为None时,进行的操作大体相同;只是_initial_balance会做一些初始化的操作;而真正执行rebalance操作动作的是_reassign_parts方法;
具体来看代码:
- try:
- # get_balance:获取执行重新平衡操作之前的ring的balance的值;
- # balance的值具体解释如下:
- # 比如一个device至少需要123个partitions,而目前存在的是124个partitions,则balance的值计算如下:
- # (124-123)/123
- # 返回获取的balance的值给last_balance;
- last_balance = builder.get_balance()
- parts, balance = builder.rebalance(seed=get_seed(3))
- except exceptions.RingBuilderError, e:
- ......
- exit(EXIT_ERROR)
复制代码
首先调用方法get_balance来获取执行重新平衡操作之前的ring的balance的值。具体来看方法get_balance,看看这个值是怎么定义和计算的(注释解析的很清楚了):
- def get_balance(self):
- """
- 获取ring的balance的值;
- balance的值具体解释如下:
- 比如一个device至少需要123个partitions,而目前存在的是124个partitions,则balance的值计算如下:
- (124-123)/123
- 返回获取的balance的值;
- """
- balance = 0
-
- # 计算并获取一个分区的权重(weight);
- # 从所有设备的总权重(weight)中,返回计算出来的每一个分区的权重(weight);
- # 计算方法就是将partition数目乘以副本数得到总的partition数目,然后除以现有dev的weight总和,得到每个partition的权重;
- # 实际上得到的是单位权重对应的partition数目;
- # return self.parts * self.replicas / sum(d['weight'] for d in self._iter_devs())
- weight_of_one_part = self.weight_of_one_part()
- for dev in self._iter_devs():
- if not dev['weight']:
- if dev['parts']:
- balance = 999.99
- break
- continue
- # 计算blance的值;
- # dev['parts']表示目前所存在的partitions的数目;
- # dev['weight'] * weight_of_one_part表示的是根据dev['weight']计算出来的此device所需要的partitions的数目;
- dev_balance = abs(100.0 * dev['parts'] / (dev['weight'] * weight_of_one_part) - 100.0)
- # 取得和原来相比数值较高的值,赋值给balance;
- if dev_balance > balance:
- balance = dev_balance
- return balance
复制代码
其次,调用类RingBuilder(object)中的方法rebalance来实现执行重新平衡ring操作,并返回发生变动的parts数目和新的平衡比变量balance。
- def rebalance(self, seed=None):
- """
- 执行重新平衡ring操作;
- 这个方法是builder中重要的方法,因为它会对ring上的devices执行分配和重新分配的分区的操作,基于权重、zones和最近执行的重新分配信息等;
-
- 程序会根据_last_part_moves_epoch是否为None来决定,程序执行的路线;
- 如果为None(说明是第一次rebalance),程序会调用_initial_balance()方法,然后返回结果;
- 其实它的操作跟_last_part_moves_epoch不为None时,进行的操作大体相同;
- 只是_initial_balance会做一些初始化的操作;
- 而真正执行rebalance操作动作的是_reassign_parts方法;
- 返回发生变动的parts数目和新的平衡比变量balance;
- """
- if seed:
- random.seed(seed)
- # 令实例中的ring为空
- self._ring = None
-
- # _last_part_moves_epoch:表示时间的偏移量;
- # 表示partition最后一次迁移时间;
- # 如果这个值为None,则在执行平衡操作之前要进行相关变量的初始化,所以调用方法_initial_balance;
- if self._last_part_moves_epoch is None:
- # 增加一些初始化设置的balance方法;
- # 跟rebalance方法实现的功能是一样的,不过就是增加一些初始化方法;
- self._initial_balance()
- # devs_changed:表明如果设备信息自上一次平衡后已经改变,则赋值为true;
- self.devs_changed = False
-
- # 分区数目:self.parts = 2 ** self.part_power;
- # get_balance:获取ring的balance的值;
- # balance的值具体解释如下:
- # 比如一个device至少需要123个partitions,而目前存在的是124个partitions,则balance的值计算如下:
- # (124-123)/123
- return self.parts, self.get_balance()
-
- retval = 0
-
- # 更新part moved时间;
- # 即更新_last_part_moves_epoch;
- self._update_last_part_moves()
- last_balance = 0
-
- # _adjust_replica2part2dev_size:调整_replica2part2dev的大小,确保_replica2part2dev中的序列长度是正确的,相对于self.replicas当前的值;
- # 返回包含两个元素的元组:
- # 第一个元素是一个列表(partition,replicas)表示哪些副本需要(重新)分配给设备;
- # 第二个元素是一个计数器,表示多少副本需要被move;
- new_parts, removed_part_count = self._adjust_replica2part2dev_size()
-
- # 增加需要被move的副本数目;
- retval += removed_part_count
-
- # 调用 _reassign_parts 方法,顾名思义,就是从新分配parts的过程;
- # 无论是第一次rebalance还是修改后重新rebalance ,最终都是通过这个函数。
- self._reassign_parts(new_parts)
-
- # 增加需要新分配dev的part的数目;
- retval += len(new_parts)
-
- while True:
- # 返回一个list(part,replica)对,需要重新分配;
- reassign_parts = self._gather_reassign_parts()
- # 重新分配的实际动作;
- self._reassign_parts(reassign_parts)
- # 增加需要新分配dev的part的数目;
- retval += len(reassign_parts)
-
- while self._remove_devs:
- # 删除相应的dev;
- self.devs[self._remove_devs.pop()['id']] = None
-
- # 获取新的平衡比;
- # 获取ring的balance的值;
- balance = self.get_balance()
-
- if balance
复制代码
解析一下这个重要的方法:
(1)看语句:
if self._last_part_moves_epoch is None:
self._initial_balance()
self.devs_changed = False
return self.parts, self.get_balance()
_last_part_moves_epoch:表示partition最后一次迁移时间;
如果_last_part_moves_epoch值为None,则调用方法_initial_balance来完成ring重平衡的操作,然后返回self.parts和方法get_balance获取的平衡比值;
方法_initial_balance完全的实现了ring的重平衡操作,所不同的是,方法中增加对变量_last_part_moves和_last_part_moves_epoch初始化的过程;
- def _initial_balance(self):
- """
- 初始化分区的分配,和重新平衡一个存在的ring是一样的,只不过要事先进行一些初始化操作;
- """
- self._last_part_moves = array('B', (0 for _junk in xrange(self.parts)))
- self._last_part_moves_epoch = int(time())
- self._reassign_parts(self._adjust_replica2part2dev_size()[0])
复制代码
(2)看代码:
retval = 0
self._update_last_part_moves()
last_balance = 0
new_parts, removed_part_count = self._adjust_replica2part2dev_size()
retval += removed_part_count
self._reassign_parts(new_parts)
retval += len(new_parts)
首先调用方法_update_last_part_moves来实现更新part moved时间,进入这个方法可以看到,具体就是更新了变量_last_part_moves_epoch和_last_part_moves。
方法_adjust_replica2part2dev_size返回了两个值new_parts和removed_part_count。第一个值具体是一个列表(partition,replicas),表示哪些副本需要(重新)分配给设备;第二个值是一个计数器,表示多少副本需要被move。
调用方法_reassign_parts实现对new_parts(需要分配给设备的副本)重新分配parts的过程;
retval表示发生变动的parts数目;
(3)看代码:
- while True:
- reassign_parts = self._gather_reassign_parts()
- self._reassign_parts(reassign_parts)
- retval += len(reassign_parts)
- while self._remove_devs:
- self.devs[self._remove_devs.pop()['id']] = None
- balance = self.get_balance()
- if balance
复制代码
这里其实和(2)中的处理是一致的,在swift前面的版本中,没有(2)的那一部分,就我理解而言,(2)也许是做了一个预处理的意思(有时间需要深入分析一下);
这里调用了方法_gather_reassign_parts返回一个list(part,replica)对,表示需要重新分配的副本。接下来调用方法_reassign_parts实现对reassign_parts重新分配的过程。在完成对重新分配之后,获取新的平衡比,当循环满足一定条件之后跳出(前面两个跳出条件还没有很好的理解,而retval
== self.parts说明,所有的parts都被move过了,当然这种情况一般不会出现)。
(4)方法的最后返回了表示新的平衡比变量balance和表示被迁移和重新分配dev的part数目的变量retval;
再回到最初的方法rebalance中,有这样一条语句:
复制代码
这是验证ring正确性的一个方法验证环正确的方法,方法确保分区已分配到实际设备,而没有出现双重分配等错误;
方法rebalance的最后是一些文件保存的操作;
至此,swift-ring-builder中的方法rebalance也解析完成。
至此,swift-ring-builder中比较重要的操作方法都解析完成了,其他都是很好理解的方法。
博文中不免有不正确的地方,欢迎朋友们不吝批评指正,谢谢大家了!
上一篇
OpenStack Swift源码分析(5)----swift-ring-builder源代码解析之一
|