tntzbzc 发表于 2014-11-20 15:34:59

OpenStack Swift源码分析(5)----swift-ring-builder源代码解析之二

本帖最后由 pig2 于 2014-11-21 15:13 编辑

问题导读
1.那个方法实现了从环ring中删除dev_id指定的device?
2.rebalance方法实现了什么功能?

static/image/hrline/4.gif







上一篇
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的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] = 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] = 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())


    (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] = 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中,有这样一条语句:
builder.validate()

    这是验证ring正确性的一个方法验证环正确的方法,方法确保分区已分配到实际设备,而没有出现双重分配等错误;
    方法rebalance的最后是一些文件保存的操作;
    至此,swift-ring-builder中的方法rebalance也解析完成。
    至此,swift-ring-builder中比较重要的操作方法都解析完成了,其他都是很好理解的方法。
    博文中不免有不正确的地方,欢迎朋友们不吝批评指正,谢谢大家了!

上一篇
OpenStack Swift源码分析(5)----swift-ring-builder源代码解析之一

页: [1]
查看完整版本: OpenStack Swift源码分析(5)----swift-ring-builder源代码解析之二