天天看点

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

感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!

如果转载,请保留作者信息。

博客地址:http://blog.csdn.net/gaoxingnengjisuan

邮箱地址:[email protected]

    接续上一篇博文,继续解析文件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 or abs(last_balance - balance) < 1 or retval == self.parts:
                break
            
            last_balance = balance
            
        self.devs_changed = False
        
        self.version += 1
        
        # 返回表示新的平衡比变量balance和表示被迁移和重新分配dev的part数目的变量retval;
        return retval, 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 < 1 or abs(last_balance - balance) < 1 or retval == self.parts:

                break            

            last_balance = 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源代码解析之二

继续阅读