分享

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

tntzbzc 发表于 2014-11-20 15:34:59 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 18100
本帖最后由 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:
  1.   def remove_dev(self, dev_id):
  2.         """
  3.         从环ring中移除一个设备device;
  4.         """
  5.         # 根据dev_id获取指定的dev的id;
  6.         dev = self.devs[dev_id]
  7.         # 设置准备删除dev的weight的值为0;
  8.         dev['weight'] = 0
  9.         # 记录所删除的dev到列表中;
  10.         self._remove_devs.append(dev)
  11.         # _set_parts_wanted:方法根据dev的weight计算dev除了目前已经分配的partition数目而外,还要分配的partition数目;
  12.         self._set_parts_wanted()
  13.         self.devs_changed = True
  14.         self.version += 1
复制代码


    这个方法实现了从环ring中删除dev_id指定的device;

    7.来看方法rebalance:
   
  1. def rebalance(self, seed=None):  
  2.     if seed:  
  3.         random.seed(seed)  
  4.   
  5.     # 令实例中的ring为空  
  6.     self._ring = None  
  7.       
  8.     # _last_part_moves_epoch:表示时间的偏移量;  
  9.     if self._last_part_moves_epoch is None:  
  10.         # 增加一些初始化设置的balance方法;  
  11.         self._initial_balance()  
  12.         # devs_changed:表明如果设备信息自上一次平衡后已经改变,则赋值为true;  
  13.         self.devs_changed = False  
  14.          
  15.         # 分区数目:self.parts = 2 ** self.part_power;  
  16.         # get_balance:获取ring的balance的值;  
  17.         # balance的值具体解释如下:  
  18.         # 比如一个device至少需要123个partitions,而目前存在的是124个partitions,则balance的值计算如下:  
  19.         # (124-123)/123  
  20.         return self.parts, self.get_balance()  
  21.     retval = 0  
  22.       
  23.     # 更新part moved时间;  
  24.     self._update_last_part_moves()  
  25.     last_balance = 0  
  26.       
  27.     # _adjust_replica2part2dev_size:调整_replica2part2dev的大小,确保_replica2part2dev中的序列长度是正确的,相对于self.replicas当前的值;  
  28.     # 返回包含两个元素的元组:  
  29.     # 第一个元素是一个列表(partition,replicas)表示哪些副本需要(重新)分配给设备;  
  30.     # 第二个元素是一个计数器,表示多少副本需要被move;  
  31.     new_parts, removed_part_count = self._adjust_replica2part2dev_size()  
  32.     retval += removed_part_count  
  33.     self._reassign_parts(new_parts)  
  34.     retval += len(new_parts)  
  35.     while True:  
  36.         # 返回一个list(part,replica)对,需要重新分配;  
  37.         reassign_parts = self._gather_reassign_parts()  
  38.         # 重新分配的实际动作;  
  39.         self._reassign_parts(reassign_parts)  
  40.         retval += len(reassign_parts)  
  41.          
  42.         while self._remove_devs:  
  43.             # 删除相应的dev;  
  44.             self.devs[self._remove_devs.pop()['id']] = None  
  45.         # 获取新的平衡比;  
  46.         balance = self.get_balance()  
  47.         if balance < 1 or abs(last_balance - balance) < 1 or retval == self.parts:  
  48.             break  
  49.         last_balance = balance  
  50.     self.devs_changed = False  
  51.     self.version += 1  
  52.     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方法;
    具体来看代码:
      
  1. try:           
  2.             # get_balance:获取执行重新平衡操作之前的ring的balance的值;
  3.             # balance的值具体解释如下:
  4.             # 比如一个device至少需要123个partitions,而目前存在的是124个partitions,则balance的值计算如下:
  5.             # (124-123)/123
  6.             # 返回获取的balance的值给last_balance;
  7.             last_balance = builder.get_balance()
  8.             parts, balance = builder.rebalance(seed=get_seed(3))
  9.         except exceptions.RingBuilderError, e:
  10.             ......
  11.             exit(EXIT_ERROR)
复制代码


首先调用方法get_balance来获取执行重新平衡操作之前的ring的balance的值。具体来看方法get_balance,看看这个值是怎么定义和计算的(注释解析的很清楚了):
  1. def get_balance(self):
  2.         """        
  3.         获取ring的balance的值;
  4.         balance的值具体解释如下:
  5.         比如一个device至少需要123个partitions,而目前存在的是124个partitions,则balance的值计算如下:
  6.         (124-123)/123
  7.         返回获取的balance的值;
  8.         """
  9.         balance = 0
  10.         
  11.         # 计算并获取一个分区的权重(weight);
  12.         # 从所有设备的总权重(weight)中,返回计算出来的每一个分区的权重(weight);
  13.         # 计算方法就是将partition数目乘以副本数得到总的partition数目,然后除以现有dev的weight总和,得到每个partition的权重;
  14.         # 实际上得到的是单位权重对应的partition数目;
  15.         # return self.parts * self.replicas / sum(d['weight'] for d in self._iter_devs())
  16.         weight_of_one_part = self.weight_of_one_part()
  17.         for dev in self._iter_devs():
  18.             if not dev['weight']:
  19.                 if dev['parts']:
  20.                     balance = 999.99
  21.                     break
  22.                 continue
  23.             # 计算blance的值;
  24.             # dev['parts']表示目前所存在的partitions的数目;
  25.             # dev['weight'] * weight_of_one_part表示的是根据dev['weight']计算出来的此device所需要的partitions的数目;
  26.             dev_balance = abs(100.0 * dev['parts'] / (dev['weight'] * weight_of_one_part) - 100.0)
  27.             # 取得和原来相比数值较高的值,赋值给balance;
  28.             if dev_balance > balance:
  29.                 balance = dev_balance
  30.         return balance   
复制代码




其次,调用类RingBuilder(object)中的方法rebalance来实现执行重新平衡ring操作,并返回发生变动的parts数目和新的平衡比变量balance。


  1. def rebalance(self, seed=None):
  2.         """
  3.         执行重新平衡ring操作;
  4.         这个方法是builder中重要的方法,因为它会对ring上的devices执行分配和重新分配的分区的操作,基于权重、zones和最近执行的重新分配信息等;
  5.         
  6.         程序会根据_last_part_moves_epoch是否为None来决定,程序执行的路线;
  7.         如果为None(说明是第一次rebalance),程序会调用_initial_balance()方法,然后返回结果;
  8.         其实它的操作跟_last_part_moves_epoch不为None时,进行的操作大体相同;
  9.         只是_initial_balance会做一些初始化的操作;
  10.         而真正执行rebalance操作动作的是_reassign_parts方法;
  11.         返回发生变动的parts数目和新的平衡比变量balance;
  12.         """
  13.         if seed:
  14.             random.seed(seed)
  15.         # 令实例中的ring为空
  16.         self._ring = None
  17.         
  18.         # _last_part_moves_epoch:表示时间的偏移量;
  19.         # 表示partition最后一次迁移时间;
  20.         # 如果这个值为None,则在执行平衡操作之前要进行相关变量的初始化,所以调用方法_initial_balance;
  21.         if self._last_part_moves_epoch is None:
  22.             # 增加一些初始化设置的balance方法;
  23.             # 跟rebalance方法实现的功能是一样的,不过就是增加一些初始化方法;
  24.             self._initial_balance()
  25.             # devs_changed:表明如果设备信息自上一次平衡后已经改变,则赋值为true;
  26.             self.devs_changed = False
  27.             
  28.             # 分区数目:self.parts = 2 ** self.part_power;
  29.             # get_balance:获取ring的balance的值;
  30.             # balance的值具体解释如下:
  31.             # 比如一个device至少需要123个partitions,而目前存在的是124个partitions,则balance的值计算如下:
  32.             # (124-123)/123
  33.             return self.parts, self.get_balance()
  34.         
  35.         retval = 0
  36.         
  37.         # 更新part moved时间;
  38.         # 即更新_last_part_moves_epoch;
  39.         self._update_last_part_moves()
  40.         last_balance = 0
  41.         
  42.         # _adjust_replica2part2dev_size:调整_replica2part2dev的大小,确保_replica2part2dev中的序列长度是正确的,相对于self.replicas当前的值;
  43.         # 返回包含两个元素的元组:
  44.         # 第一个元素是一个列表(partition,replicas)表示哪些副本需要(重新)分配给设备;
  45.         # 第二个元素是一个计数器,表示多少副本需要被move;
  46.         new_parts, removed_part_count = self._adjust_replica2part2dev_size()
  47.         
  48.         # 增加需要被move的副本数目;
  49.         retval += removed_part_count
  50.         
  51.         # 调用 _reassign_parts 方法,顾名思义,就是从新分配parts的过程;
  52.         # 无论是第一次rebalance还是修改后重新rebalance ,最终都是通过这个函数。
  53.         self._reassign_parts(new_parts)
  54.         
  55.         # 增加需要新分配dev的part的数目;
  56.         retval += len(new_parts)
  57.         
  58.         while True:
  59.             # 返回一个list(part,replica)对,需要重新分配;
  60.             reassign_parts = self._gather_reassign_parts()
  61.             # 重新分配的实际动作;
  62.             self._reassign_parts(reassign_parts)
  63.             # 增加需要新分配dev的part的数目;
  64.             retval += len(reassign_parts)
  65.             
  66.             while self._remove_devs:
  67.                 # 删除相应的dev;
  68.                 self.devs[self._remove_devs.pop()['id']] = None
  69.             
  70.             # 获取新的平衡比;
  71.             # 获取ring的balance的值;
  72.             balance = self.get_balance()
  73.             
  74.             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初始化的过程;

  1. def _initial_balance(self):
  2.         """
  3.         初始化分区的分配,和重新平衡一个存在的ring是一样的,只不过要事先进行一些初始化操作;
  4.         """
  5.         self._last_part_moves = array('B', (0 for _junk in xrange(self.parts)))
  6.         self._last_part_moves_epoch = int(time())
  7.         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)看代码:
  
  1. while True:
  2.             reassign_parts = self._gather_reassign_parts()
  3.             self._reassign_parts(reassign_parts)
  4.             retval += len(reassign_parts)            
  5.             while self._remove_devs:
  6.                 self.devs[self._remove_devs.pop()['id']] = None            
  7.             balance = self.get_balance()            
  8.             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中,有这样一条语句:
  1. builder.validate()
复制代码


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

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

没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条