• 执行swift-ring-build的rebalance命令,会首先调用下面函数 def rebalance(): def get_seed(index): try: return argv[index] except IndexError: pass ...
    执行swift-ring-build的rebalance命令,会首先调用下面函数
       
     def rebalance():
            def get_seed(index):
                try:
                    return argv[index]
                except IndexError:
                    pass
            devs_changed = builder.devs_changed
            try:
                last_balance = builder.get_balance()
                #调用RingBuilder类的rebalance()函数
                parts, balance = builder.rebalance(seed=get_seed(3))
            except exceptions.RingBuilderError as e:
                print '-' * 79
                print("An error has occurred during ring validation. Common\n"
                      "causes of failure are rings that are empty or do not\n"
                      "have enough devices to accommodate the replica count.\n"
                      "Original exception message:\n %s" % e.message
                      )
                print '-' * 79
                exit(EXIT_ERROR)
            if not parts:
                #没有partiton需要移动的情况有两种,一种是经过rebalance的计算,的确没有partition需要移动。
                #另外一种是由于min_part_hours参数的限制,在min_part_hours时间内只允许移动一个partition。
                print 'No partitions could be reassigned.'
                print 'Either none need to be or none can be due to ' \
                      'min_part_hours [%s].' % builder.min_part_hours
                exit(EXIT_WARNING)
            if not devs_changed and abs(last_balance - balance) < 1 and \
                    not (last_balance == MAX_BALANCE and balance == MAX_BALANCE):
                print 'Cowardly refusing to save rebalance as it did not change ' \
                      'at least 1%.'
                exit(EXIT_WARNING)
            try:
                #验证生成的Ring的一致性
                builder.validate()
            except exceptions.RingValidationError as e:
                print '-' * 79
                print("An error has occurred during ring validation. Common\n"
                      "causes of failure are rings that are empty or do not\n"
                      "have enough devices to accommodate the replica count.\n"
                      "Original exception message:\n %s" % e.message
                      )
                print '-' * 79
                exit(EXIT_ERROR)
            print 'Reassigned %d (%.02f%%) partitions. Balance is now %.02f.' % \
                  (parts, 100.0 * parts / builder.parts, balance)
            status = EXIT_SUCCESS
            #如果balance的值大于5,这意味着此时的Ring处于一个非常不稳定的状态。
            #需要在min_part_hours时间之后,再次rebalance
            if balance > 5:
                print '-' * 79
                print 'NOTE: Balance of %.02f indicates you should push this ' % \
                      balance
                print ' ring, wait at least %d hours, and rebalance/repush.' \
                      % builder.min_part_hours
                print '-' * 79
                status = EXIT_WARNING
            ts = time()
            #将builder文件和reblance之后生成的ring文件存放到backup目录下。
            builder.get_ring().save(
                pathjoin(backup_dir, '%d.' % ts + basename(ring_file)))
            builder.save(pathjoin(backup_dir, '%d.' % ts + basename(argv[1])))
            builder.get_ring().save(ring_file)
            builder.save(argv[1])
            exit(status)
    这个函数首先调用swift.common.ring.builder.RingBuilder类的get_balance()函数来获取当前Ring的balance值,这个值标识了一个Ring的平衡程度,也就是健康状态,这个值越高表示这个Ring的balance越需要rebalance。一个健康的Ring的balance的值应该是0
    Ring的balance的值取决于所有device的balance值,一个device的balance的值指的是超过这个device所希望接纳的partition个数的partition的数量,除以该device所希望接纳的partition的个数,然后乘以100.比如一个device所希望接纳partition个数是123,结果现在它接纳了124个partition,那么这个device的balance的值为(124-123)/123*100=0.83。在这个Ring中,取所有device的balance的值的最大值作为该Ring的balance值。
    如果Ring没有device的变化(添加或删除),并且rebalance之前和之后的balance的值相差小于1,则认为该Ring不需要rebalance,不会生成新的ring文件。
    rebalance命令的实际工作仍是由swift.common.ring.builder.RingBuilder类的rebalance()来完成。
    #swift/common/ring/builder.py
        def rebalance(self, seed=None):
            """
            这是Ringbuild的主要功能函数,它会根据设备权重、zone的信息(尽可能地将partition的副本分配到不在一个zone的设备上),
            以及近期的分配情况等信息,重新对partition进行分配。
            这个函数并不是partition分配的最佳算法(最佳算法会进行更多的分析从而占用更多的时间)
            此函数会一直做rebalance操作直到这个Ring的balance值小于1%,或者balance的值变化小于1%
            """
            old_replica2part2dev = copy.deepcopy(self._replica2part2dev)
            if seed is not None:
                random.seed(seed)
            self._ring = None
            if self._last_part_moves_epoch is None:
                      #对于新创建的Ring执行_initial_balance()
                self._initial_balance()
                self.devs_changed = False
                return self.parts, self.get_balance()
            changed_parts = 0
            self._update_last_part_moves()
            last_balance = 0
            #函数_adjust_replica2part2dev_size()的主要功能是调整设备查询表
            #也就是_replica2part2dev数组,使其大小和维度调整为和当前的replicas数量以及partition数量一致
            #并且返回需要新添加的partition与replicas列表
            new_parts, removed_part_count = self._adjust_replica2part2dev_size()
            changed_parts += removed_part_count
            if new_parts or removed_part_count:
                self._set_parts_wanted()
            self._reassign_parts(new_parts)
            changed_parts += len(new_parts)
            while True:
                reassign_parts = self._gather_reassign_parts()
                self._reassign_parts(reassign_parts)
                changed_parts += 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 \
                        changed_parts == self.parts:
                    break
                last_balance = balance
            self.devs_changed = False
            self.version += 1
            changed_parts = 0
            for rep_id, _rep in enumerate(self._replica2part2dev):
                for part_id, new_device in enumerate(_rep):
                    # IndexErrors will be raised if the replicas are increased or
                    # decreased, and that actually means the partition has changed
                    try:
                        old_device = old_replica2part2dev[rep_id][part_id]
                    except IndexError:
                        changed_parts += 1
                        continue
                    if old_device != new_device:
                        changed_parts += 1
            return changed_parts, balance
    如果是新创建的Ring,那么控制逻辑进入到_initial_balance()函数中。如果不是新创建的Ring,会直接调用_reassign_parts(),反复对partitions进行重新分配,直到balance值小于1为止(或者两次rebalance操作得到的balance值的变化小于1%,或者所有的partition都已经移动过一次)。
    新建Ring和非新建Ring的rebalance操作过程类似,区别只是在于新建Ring的情况在_initial_balance()函数里做了初始化工作。
         def _initial_balance(self):
            """
            Initial partition assignment is the same as rebalancing an
            existing ring, but with some initial setup beforehand.
            """
            self._last_part_moves = array('B', (0 for _junk in xrange(self.parts)))
            self._last_part_moves_epoch = int(time())
            self._set_parts_wanted()
            self._reassign_parts(self._adjust_replica2part2dev_size()[0])
    对于新建Ring的情况,_adjust_replica2part2dev_size()所返回的tuple的第一个值是一个这样的数组,该数组的每一个元素是一个(partition,replica)的tuple,表示该partition的replica需要被重新分配到device上去。
    _reassign_parts()函数以上面返回的数组为参数进行分配。
    _reassign_parts()首先将设备按照每个设备还想接收多少个partiton(根据weight)来排队,并且在rebalance的过程中一直按照这个标准队列,然后遍历整个partition的列表,将partition分配到想接收partition最多的设备上,并且同时保证这个partition的副本之间距离最远。
    所谓距离最远指的是副本之间尽可能地在不同的region里面,如果一定要在同一个region里面,尽可能地分配到不同zone里面,如果没有满足的情况,就尽可能地分配到具有不同的(IP地址、端口)的设备上。
    Rebalance()最后将新生成的Ring文件保存以及备份,整个Ring生成过程就完成了。Ring rebalance的过程仅仅是生成不同的Ring文件,也就是修改partition到device的映射,最终partition的移动是由Replicator进程来完成的。
    参考
    展开全文
  • 接续上一篇博文,继续解析文件swift-ring-builder。 来看方法remove_dev: def remove_dev(self, dev_id): """ 从环ring中移除一个设备device; """ # 根据dev_id获取指定的dev的id; dev = self.devs[dev_i

    感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
    如果转载,请保留作者信息。
    博客地址:http://blog.csdn.net/gaoxingnengjisuan
    邮箱地址:dong.liu@siat.ac.cn


        接续上一篇博文,继续解析文件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中比较重要的操作方法都解析完成了,其他都是很好理解的方法。

        博文中不免有不正确的地方,欢迎朋友们不吝批评指正,谢谢大家了!微笑

    展开全文
  • 工作比较忙,几天没有写博客了,今天开始同时总结一下之前看SWIFT模块的源码。 从头开始,部署完成swift以后,就可以执行命令swift-init main start来执行swift的初始化工作。这里实际上执行的是/bin/swift-init中的...

    感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
    如果转载,请保留作者信息。
    博客地址:http://blog.csdn.net/gaoxingnengjisuan
    邮箱地址:dong.liu@siat.ac.cn


        工作比较忙,几天没有写博客了,今天开始同时总结一下之前看SWIFT模块的源码,另一部分NOVA源码的解析也会继续进行。

        从头开始,部署完成swift以后,就可以执行命令swift-init main start来执行swift的初始化工作。这里实际上执行的是/bin/swift-init中的main方法,下面我们开始看代码:

    if __name__ == "__main__":
        sys.exit(main())
        进一步调用方法main:

    def main():
        parser = OptionParser(USAGE)
        parser.add_option('-v', '--verbose', action="store_true",
                          default=False, help="display verbose output")
        parser.add_option('-w', '--no-wait', action="store_false", dest="wait",
                          default=True, help="won't wait for server to start "
                          "before returning")
        parser.add_option('-o', '--once', action="store_true",
                          default=False, help="only run one pass of daemon")
        # this is a negative option, default is options.daemon = True
        parser.add_option('-n', '--no-daemon', action="store_false", dest="daemon",
                          default=True, help="start server interactively")
        parser.add_option('-g', '--graceful', action="store_true",
                          default=False, help="send SIGHUP to supporting servers")
        parser.add_option('-c', '--config-num', metavar="N", type="int",
                          dest="number", default=0,
                          help="send command to the Nth server only")
        parser.add_option('-k', '--kill-wait', metavar="N", type="int",
                          dest="kill_wait", default=KILL_WAIT,
                          help="wait N seconds for processes to die (default 15)")
        parser.add_option('-r', '--run-dir', type="str",
                          dest="run_dir", default=RUN_DIR,
                          help="alternative directory to store running pid files "
                          "default: %s" % RUN_DIR)
    
        options, args = parser.parse_args()
    
        if len(args) < 2:
            parser.print_help()
            print 'ERROR: specify server(s) and command'
            return 1
    
        # 在这里对命令和参数进行了分割,从而决定接下来的执行任务:
        # command此处为start;
        # servers此处为['swift-init','main'];
        command = args[-1]
        servers = args[:-1]
    
        # this is just a silly swap for me cause I always try to "start main"
        commands = dict(Manager.list_commands()).keys()
        if command not in commands and servers[0] in commands:
            servers.append(command)
            command = servers.pop(0)
    
        # 实例化类Manager;
        # Manager是直接管理各个servers的类;
        # 初始化各个服务;
        manager = Manager(servers, run_dir=options.run_dir)
        try:
            status = manager.run_command(command, **options.__dict__)
        except UnknownCommandError:
            parser.print_help()
            print 'ERROR: unknown command, %s' % command
            status = 1
    
        return 1 if status else 0
        看其中的一段代码:

    # list_commands:获取所有可公开访问的命令;
        commands = dict(Manager.list_commands()).keys()
        if command not in commands and servers[0] in commands:
            servers.append(command)
            command = servers.pop(0)
        经过调试输出:

        commands = ['status', 'start', 'force-reload', 'stop', 'no-wait', 'no-daemon', 'reload', 'shutdown', 'restart', 'once']

    # 实例化类Manager;
    # Manager是直接管理各个servers的类;
    # 初始化各个服务;
    manager = Manager(servers, run_dir=options.run_dir)
        其中Manager是管理各个server执行操作的类,这条语句实现了对类Manager的初始化并获取实力对象;

        具体来看:

    class Manager():
        """
        管理各个server执行操作的类;
        param servers:服务名称列表;
        """
    
        def __init__(self, servers, run_dir=RUN_DIR):
            server_names = set()
            for server in servers:
                if server == 'all':
                    server_names.update(ALL_SERVERS)
                # MAIN_SERVERS = ['proxy-server', 'account-server', 'container-server', 'object-server']
                elif server == 'main':
                    server_names.update(MAIN_SERVERS)
                elif server == 'rest':
                    server_names.update(REST_SERVERS)
                elif '*' in server:
                    # convert glob to regex
                    server_names.update([s for s in ALL_SERVERS if re.match(server.replace('*', '.*'), s)])
                else:
                    server_names.add(server)
    
            self.servers = set()
            # 如果servers中包含‘main’:
            # server_names = ['proxy-server', 'account-server', 'container-server', 'object-server'];
            # run_dir = /var/run/swift;
            # Server:依次初始化四个服务;
            for name in server_names:
                self.servers.add(Server(name, run_dir))
        我们前面测试得到servers = ['swift-init', 'main'],可见server_names = ['proxy-server', 'account-server', 'container-server', 'object-server'],这是swift四个服务的名称;

        所以这个初始化方法根据传入servers的参数值,确定了server_names的值;

        再回到/bin/swift-init中的main方法:

        try:
            status = manager.run_command(command, **options.__dict__)
        except UnknownCommandError:
            parser.print_help()
            print 'ERROR: unknown command, %s' % command
            status = 1
        其中:

        command = start

        options = {'run_dir': '/var/run/swift', 'daemon': True, 'verbose': False, 'number': 0, 'kill_wait': 15, 'graceful': False, 'wait': True, 'once': False}

        进一步看方法run_command:

    def run_command(self, cmd, **kwargs):
        """
        找到命令名称并运行它;
        """
        f = self.get_command(cmd)
        return f(**kwargs)
        再看方法get_command:

        def get_command(self, cmd):
            """
            查找并返回cmd指定的方法名称;
            """
            cmd = cmd.lower().replace('-', '_')
            try:
                f = getattr(self, cmd)
            except AttributeError:
                raise UnknownCommandError(cmd)
            if not hasattr(f, 'publicly_accessible'):
                raise UnknownCommandError(cmd)
            return f
        可以知道通过这两个方法的结合,可以定位到cmd指定的方法,并进一步运行它;

        这里具体调用并执行的是def start(self, **kwargs):

    def start(self, **kwargs):
            """
            启动一个服务;
            """
            setup_env()
            status = 0
    
            for server in self.servers:
                server.launch(**kwargs)
            if not kwargs.get('daemon', True):
                for server in self.servers:
                    try:
                        status += server.interact(**kwargs)
                    except KeyboardInterrupt:
                        print _('\nuser quit')
                        self.stop(**kwargs)
                        break
            elif kwargs.get('wait', True):
                for server in self.servers:
                    status += server.wait(**kwargs)
            return status
        这个方法中将会依次启动servers指定的服务,即servers = ['proxy-server', 'account-server', 'container-server', 'object-server'];

        具体实现将会在下一篇博文中进行解析。

        博文中不免有不正确的地方,欢迎朋友们不吝批评指正,谢谢大家了!微笑

    展开全文
  • 感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正! 如果转载,请保留作者信息。 博客地址:... ...PS:最近没有登录博客,很多朋友的留言没有看见,这里道歉!...

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

    如果转载,请保留作者信息。
    博客地址:http://blog.csdn.net/gaoxingnengjisuan
    邮箱地址:dong.liu@siat.ac.cn

    PS:最近没有登录博客,很多朋友的留言没有看见,这里道歉!还有就是本人较少上QQ,可以邮件交流。


    概述部分:

    实现运行数据同步进程的操作;
    获取对象环确定的本地设备下需要进行数据验证和数据同步(修复)操作的分区的任务列表;
    针对rebalance操作后数据同步情况和某些对象数据损坏情况,
    分别实现同步本地分区数据到远程副本相应分区的对象数据操作;
    主要用于rebalance操作后的数据同步和数据损坏后的数据修复工作;


    源码解析部分:

    下面是这部分代码的主要执行流程,代码中较重要的部分已经进行了相关的注释;

    from swift.obj.replicator import ObjectReplicator
    from swift.common.utils import parse_options
    from swift.common.daemon import run_daemon
    from optparse import OptionParser
    
    if __name__ == '__main__':
        parser = OptionParser("%prog CONFIG [options]")
        parser.add_option('-d', '--devices',
                          help='Replicate only given devices. '
                               'Comma-separated list')
        parser.add_option('-p', '--partitions',
                          help='Replicate only given partitions. '
                               'Comma-separated list')
        conf_file, options = parse_options(parser=parser, once=True)
        run_daemon(ObjectReplicator, conf_file, **options)

    def run_once(self, *args, **kwargs):
        """
        实现运行数据同步进程的操作;
        获取对象环确定的本地设备下需要进行数据验证和数据同步(修复)操作的分区的任务列表;
        针对rebalance操作后数据同步情况和某些对象数据损坏情况,
        分别实现同步本地分区数据到远程副本相应分区的对象数据操作;
        主要用于rebalance操作后的数据同步和数据损坏后的数据修复工作;
        """
        start = time.time()
        self.logger.info(_("Running object replicator in script mode."))
        override_devices = list_from_csv(kwargs.get('devices'))
        override_partitions = list_from_csv(kwargs.get('partitions'))   
            
        # 获取对象环确定的本地设备下需要进行数据验证和数据同步(修复)操作的分区的任务列表;
        # 针对rebalance操作后数据同步情况和某些对象数据损坏情况,
        # 分别实现同步本地分区数据到远程副本相应分区的对象数据操作;
        # 主要用于rebalance操作后的数据同步和数据损坏后的数据修复工作;
        self.replicate(override_devices=override_devices, override_partitions=override_partitions)
        total = (time.time() - start) / 60
        self.logger.info(_("Object replication complete (once). (%.02f minutes)"), total)
        if not (override_partitions or override_devices):
            dump_recon_cache({'object_replication_time': total,
                              'object_replication_last': time.time()},
                              self.rcache, self.logger)

    def replicate(self, override_devices=None, override_partitions=None):
        """
        实现运行复制进程的操作;
            
        注释引申----object相关数据存储结构:
        # 引申1----objects文件夹下的不同分区:
        # 遍历obj_path = /srv/node/local_dev['device']/objects下的分区;
        # 示例:
        # root@kinglion-Lenovo-Product:/srv/1/node/sdb1/objects# ls  
        # 13069  133971  4799  58208  94238
            
        # 引申2:
        # 通过理解一致性hash算法可知,
        # 加入虚拟节点后每一个设备会多个虚拟节点和其对应,
        # 如果一个设备对应的分区数为n则, objects下子文件夹数目会<=n,
        # 因为存入的所有文件并不一定都能映射到当前设备所对应的分区;
            
        # 引申3----object存储结构:
        # os.listdir(obj_path)列出objects目录下的所有partition,
        # 创建object是在objects文件夹下创建objects所映射的分区号的文件夹partition,
        # 再在partition文件夹下创建以object的hash值后三位为名称的文件夹(表示不同的object),
        # 然后再在后缀文件夹下创建以object的hash值为文件夹名的文件夹(表示同一个object的不同的文件),
        # object会存储为以object上传时间戳为名.data为文件后缀的文件(表示object的数据文件);
        注:副本是针对分区来说的;
            
        获取对象环确定的本地设备下需要进行数据验证和数据同步(修复)操作的分区的任务列表;
        针对rebalance操作后数据同步情况和某些对象数据损坏情况,
        分别实现同步本地分区数据到远程副本相应分区的对象数据操作;
        主要用于rebalance操作后的数据同步和数据损坏后的数据修复工作;
        """
        self.start = time.time()
        self.suffix_count = 0
        self.suffix_sync = 0
        self.suffix_hash = 0
        self.replication_count = 0
        self.last_replication_count = -1
        self.partition_times = []
    
        if override_devices is None:
            override_devices = []
        if override_partitions is None:
            override_partitions = []
    
        # 复制操作执行过程中运行在后台的心跳进程;
        stats = eventlet.spawn(self.heartbeat)
        # 检测和处理死锁程序;
        lockup_detector = eventlet.spawn(self.detect_lockups)
        eventlet.sleep()  # Give spawns a cycle
    
        try:
            # 建立一个多线程并发池;
            self.run_pool = GreenPool(size=self.concurrency)
                
            # 获取对象环确定的本地设备下需要进行数据验证和数据同步(修复)操作的分区的任务列表;
            jobs = self.collect_jobs()
            for job in jobs:
                if override_devices and job['device'] not in override_devices:
                    continue
                if override_partitions and job['partition'] not in override_partitions:
                    continue
                    
                # dev_path = /srv/node/job['device']
                dev_path = join(self.devices_dir, job['device'])
                if self.mount_check and not ismount(dev_path):
                    self.logger.warn(_('%s is not mounted'), job['device'])
                    continue
                    
                if not self.check_ring():
                    self.logger.info(_("Ring change detected. Aborting "
                                       "current replication pass."))
                    return
                    
                # 针对rebalance操作后数据同步情况和某些对象数据损坏情况,
                # 分别实现同步本地分区数据到远程副本相应分区的对象数据操作;
                # 主要用于rebalance操作后的数据同步和数据损坏后的数据修复工作;
                if job['delete']:
                    # 同步本分区下数据到远程副本分区,并删除本分区下对象数据;
                    # 1 获取指定分区目录下各个对象的suff----suffixes;
                    # 2 遍历指定分区所有副本(除了本分区)节点,在每个副本节点上:
                    #    2.1 调用方法sync,实现通过rsync命令行实现同步本地分区下suffixes确定的若干对象数据到远程节点相应的分区下;
                    #    注意:这里并没由冗余复制数据的操作,因为命令rsync可以自动跳过完全相同的文件只更新不同的文件,大大的减低了网络传输负载;
                    #    2.2 通过REPLICATE方法获取远程节点相应的分区下对象相应的哈希值;
                    # 3 当本地分区到每个副本节点分区下的数据同步全部完成之后,则删除本分区下的数据;
                    # 注:
                    # 这里没有进行本地分区数据和远程副本数据的比较验证工作,说明这个方法需要直接同步分区下所有数据到远程副本节点;
                    # 应该适用于如下情形:
                    # 假设本分区所在设备节点号为1,所有副本设备节点号为1/2/3/4,当执行rebalance操作后,所有设备节点号为2/3/4/5,在rebalance操作过程中,
                    # 1号设备上本分区则被标志为delete;此时,需要先同步1号设备本分区数据到2/3/4/5号设备上,然后删除1号设备本分区下的数据;
                    # 在这里虽然也执行了复制1号设备数据到2/3/4号设备,但是这里并没由进行冗余复制操作,因为命令rsync可以自动跳过完全相同的文件只更新不同的文件,大大的减低了网络传输负载;
                    self.run_pool.spawn(self.update_deleted, job)
                else:
                    # 对于远程副本节点,循环执行,针对每一个节点实现以下操作:
                    # 1 通过http连接远程节点,通过REPLICATE方法实现获取job['partition']下所有对象哈希值;
                    # 2 找出本地分区下哈希值中后缀和远程分区下哈希值中后缀不同的,说明分区下的某些对象文件数据发生了变化;
                    # 3 针对发生变化的数据,调用sync方法,通过rsync命令行实现同步本地分区下若干数据到远程节点相应的分区下;
                    self.run_pool.spawn(self.update, job)
            with Timeout(self.lockup_timeout):
                self.run_pool.waitall()
        except (Exception, Timeout):
            self.logger.exception(_("Exception in top-level replication loop"))
            self.kill_coros()
        finally:
            stats.kill()
            lockup_detector.kill()
            self.stats_line()
    1.调用方法collect_jobs获取对象环确定的本地设备下需要进行数据验证和数据同步(修复)操作的分区的任务列表;
    2.遍历所有任务列表,针对rebalance操作后数据同步情况,应用方法update_deleted,实现同步本地分区数据到远程副本相应分区的对象数据操作;
    3.针对其他某些对象数据损坏情况,应用方法update,实现同步本地分区数据到远程副本相应分区的对象数据操作;


    转到1,来看方法collect_jobs的实现:

    def collect_jobs(self):
        """
        获取节点分区下的要实现数据同步操作的任务列表;
        获取对象环确定的本地设备下需要进行数据验证和数据同步(修复)操作的分区的任务列表;
            
        注释引申----object相关数据存储结构:
        # 引申1----objects文件夹下的不同分区:
        # 遍历obj_path = /srv/node/local_dev['device']/objects下的分区;
        # 示例:
        # root@kinglion-Lenovo-Product:/srv/1/node/sdb1/objects# ls  
        # 13069  133971  4799  58208  94238
            
        # 引申2:
        # 通过理解一致性hash算法可知,
        # 加入虚拟节点后每一个设备会多个虚拟节点和其对应,
        # 如果一个设备对应的分区数为n则,
        # objects下子文件夹数目会<=n,
        # 因为存入的所有文件并不一定都能映射到当前设备所对应的分区;
            
        # 引申3----object存储结构:
        # os.listdir(obj_path)列出objects目录下的所有partition,
        # 创建object是在objects文件夹下创建objects所映射的分区号的文件夹partition,
        #     例如:
        #      ls /srv/node/device1/objects/
        #      130527  165683  17635  212065  214811  252830
        # 再在partition文件夹下创建以object的hash值后三位为名称的文件夹(表示不同的object),
        #      例如:
        #      ls /srv/node/device1/objects/130527/
        #      201  hashes.pkl
        # 然后再在后缀文件夹下创建以object的hash值为文件夹名的文件夹,
        #      例如:
        #      ls /srv/node/device1/objects/130527/201/
        #      7f77f99f076c5ddbb7c7922875ab6201
        # object会存储为以object上传时间戳为名.data为文件后缀的文件(表示object的数据文件);
        #      例如:
        #      ls /srv/node/device1/objects/130527/201/7f77f99f076c5ddbb7c7922875ab6201/
        #      1405479927.09551.data
        """
        jobs = []
        ips = whataremyips()
            
        # 获取环上所有的本地设备;
        for local_dev in [dev for dev in self.object_ring.devs
                          if dev and dev['replication_ip'] in ips and
                          dev['replication_port'] == self.port]:
            # /srv/node/local_dev['device']
            dev_path = join(self.devices_dir, local_dev['device'])
            # /srv/node/local_dev['device']/objects
            obj_path = join(dev_path, 'objects')
            # /srv/node/local_dev['device']/tmp
            tmp_path = join(dev_path, 'tmp')
                
            if self.mount_check and not ismount(dev_path):
                self.logger.warn(_('%s is not mounted'), local_dev['device'])
                continue
                
            unlink_older_than(tmp_path, time.time() - self.reclaim_age)
                
            # 如果目录obj_path不存在,则建立obj_path目录;
            if not os.path.exists(obj_path):
                try:
                    mkdirs(obj_path)
                except Exception:
                    self.logger.exception('ERROR creating %s' % obj_path)
                continue
                
            # 遍历obj_path = /srv/node/local_dev['device']/objects下的分区;
            # 示例:
            # root@kinglion-Lenovo-Product:/srv/1/node/sdb1/objects# ls  
            # 13069  133971  4799  58208  94238
            # 注释引申:
            # 通过理解一致性hash算法可知,
            # 加入虚拟节点后每一个设备会多个虚拟节点和其对应,
            # 如果一个设备对应的分区数为n则,
            # obj_path下子文件夹数目会<=n,
            # 因为存入的所有文件并不一定都能映射到当前设备所对应的分区;
            # 注释引申----object存储结构:
            # os.listdir(obj_path)列出objects目录下的所有partition,
            # 创建object是在objects文件夹下创建objects所映射的分区号的文件夹partition,
            # 再在partition文件夹下创建以object的hash值后三位为名称的文件夹(表示不同的object),
            # 然后再在后缀文件夹下创建以object的hash值为文件夹名的文件夹(表示同一个object的不同的文件),
            # object会存储为以object上传时间戳为名.data为文件后缀的文件(表示object的数据文件);
            for partition in os.listdir(obj_path):
                try:
                    # /srv/node/local_dev['device']/objects/partition
                    job_path = join(obj_path, partition)
                        
                    # 如果/srv/node/local_dev['device']/objects/partition是文件而不是分区,则删除;
                    if isfile(job_path):
                        # Clean up any (probably zero-byte) files where a
                        # partition should be.
                        self.logger.warning('Removing partition directory '
                                            'which was a file: %s', job_path)
                        os.remove(job_path)
                        continue
                        
                    # 获取一个分区所有副本(replica)相关的节点信息;
                    part_nodes = self.object_ring.get_part_nodes(int(partition))
                        
                    # nodes为不是本机器nodes的其他replica-1个nodes;
                    nodes = [node for node in part_nodes
                             if node['id'] != local_dev['id']]
                        
                    jobs.append(
                        dict(
                             path=job_path,# /srv/node/local_dev['device']/objects/partition
                             device=local_dev['device'], # 本地设备;
                             nodes=nodes,
                             delete=len(nodes) > len(part_nodes) - 1,
                             partition=partition))
                except (ValueError, OSError):
                    continue
            
        # 打乱jobs的顺序;
        random.shuffle(jobs)
        if self.handoffs_first:
            # Move the handoff parts to the front of the list
            jobs.sort(key=lambda job: not job['delete'])
        self.job_count = len(jobs)
        return jobs
    1.1.获取环上所有的本地设备;
    1.2.遍历每个设备下objects下的每个分区(/srv/node/local_dev['device']/objects/partition);
    1.3.针对每个分区,获取一个分区所有副本(replica)除了本机的节点;
    1.4.每个分区的相关信息作为一个任务,返回包含所有任务的字典;


    下一篇博客将继续swift-object-replicator的分析工作。

    展开全文
  • 感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正! 如果转载,请保留作者信息。 ... ...PS:最近没有登录博客,很多朋友的留言没有看见,这里道歉!...概述部分:

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

    如果转载,请保留作者信息。
    博客地址:http://blog.csdn.net/gaoxingnengjisuan
    邮箱地址:dong.liu@siat.ac.cn

    PS:最近没有登录博客,很多朋友的留言没有看见,这里道歉!还有就是本人较少上QQ,可以邮件交流。


    概述部分:

    获取AccountBroker类的初始化对象;
    从数据库获取account的全局数据信息,包括account/created_at/put_timestamp/delete_timestamp/container_count/object_count/bytes_used/hash/id等信息;
    打印account的元数据信息;
    初始化类Ring,实现加载/etc/swift/account.ring.gz中的数据,并以此初始化类RingData;
    根据上面获取的ring的数据,获取account在ring上的位置信息;

    所以获取的数据为:

    获取account的元数据信息;
    获取account在ring上的位置信息;


    源码解析部分:

    下面是这部分代码的主要执行流程,代码中较重要的部分已经进行了相关的注释;

    import sys
    from optparse import OptionParser
    from swift.cli.info import print_info, InfoSystemExit
    
    
    if __name__ == '__main__':
        parser = OptionParser('%prog [options] ACCOUNT_DB_FILE')
        parser.add_option(
            '-d', '--swift-dir', default='/etc/swift',
            help="Pass location of swift directory")
    
        options, args = parser.parse_args()
    
        if len(args) != 1:
            sys.exit(parser.print_help())
    
        try:
            print_info('account', *args, **vars(options))
        except InfoSystemExit:
            sys.exit(1)

    def print_info(db_type, db_file, swift_dir='/etc/swift'):
        """
        打印相关信息:
        如果指定数据类型是account,则:
            获取AccountBroker类的初始化对象;
            从数据库获取account的全局数据信息,包括account/created_at/put_timestamp/delete_timestamp/container_count/object_count/bytes_used/hash/id等信息;
            打印account的元数据信息;
            初始化类Ring,实现加载/etc/swift/account.ring.gz中的数据,并以此初始化类RingData;
            根据上面获取的ring的数据,获取account在ring上的位置信息;
        如果指定数据类型是container,则:
            获取ContainerBroker类的初始化对象;
            从数据库获取container的全局数据信息,包括account/container/created_at/put_timestamp/delete_timestamp/object_count/bytes_used/reported_put_timestamp/reported_delete_timestamp/reported_object_count/reported_bytes_used/hash/id等信息;
            打印container的元数据信息;
            初始化类Ring,实现加载/etc/swift/container.ring.gz中的数据,并以此初始化类RingData;
            根据上面获取的ring的数据,获取container在ring上的位置信息;
        """
        if db_type not in ('account', 'container'):
            print "Unrecognized DB type: internal error"
            raise InfoSystemExit()
        if not os.path.exists(db_file) or not db_file.endswith('.db'):
            print "DB file doesn't exist"
            raise InfoSystemExit()
        if not db_file.startswith(('/', './')):
            db_file = './' + db_file  # don't break if the bare db file is given
        
        # 获取AccountBroker类的初始化对象;
        if db_type == 'account':
            broker = AccountBroker(db_file)
            datadir = ABDATADIR
        else:
            broker = ContainerBroker(db_file)
            datadir = CBDATADIR
        
        # 获取相关数据类型的全局信息;
        info = broker.get_info()
        account = info['account']
        container = info['container'] if db_type == 'container' else None
        
        # 打印指定数据类型的元数据信息;
        print_db_info_metadata(db_type, info, broker.metadata)
        
        # 初始化类Ring,实现加载/etc/swift/account.ring.gz中的数据,并以此初始化类RingData;
        try:
            ring = Ring(swift_dir, ring_name=db_type)
        except Exception:
            ring = None
        # 打印account或container在ring上的位置信息;
        else:
            print_ring_locations(ring, datadir, account, container)

    def print_ring_locations(ring, datadir, account, container=None):
        """
        打印account或container在ring上的位置信息;
        """
        if ring is None or datadir is None or account is None:
            raise ValueError('None type')
        storage_type = 'account'
        if container:
            storage_type = 'container'
        try:
            part, nodes = ring.get_nodes(account, container, None)
        except (ValueError, AttributeError):
            raise ValueError('Ring error')
        else:
            path_hash = hash_path(account, container, None)
            print '\nRing locations:'
            for node in nodes:
                print ('  %s:%s - /srv/node/%s/%s/%s.db' %
                       (node['ip'], node['port'], node['device'],
                        storage_directory(datadir, part, path_hash),
                        path_hash))
            print '\nnote: /srv/node is used as default value of `devices`, the ' \
                'real value is set in the %s config file on each storage node.' % \
                storage_type
    展开全文
  • 1、Replicator执行代码详细分析 上篇问中介绍了启动Replicator的具体过程,下面具体讲解Replicator的... def replicate(self, override_devices=None, override_partitions=None): """Run a replication pass""" sel
  • python-swiftclient client.py源码分析github client.py Attention:有些解释在代码注释里面,看注释!!! 先来看个实例: import client authurl = 'http://192.168.0.61:5000/v2.0' user = 'admin' key =...
  • 通过setup.cfg文件可以知道swift-ring-builder工具的源码入口位于/bin/swift-ring-builder脚本,这个脚本仅仅是对swift.cli.ringbuilder模块的封装,直接调用了swift.cli.ringbuilder中的main函数。def main...
  • 感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正! 如果转载,请保留作者信息。 博客地址:... ...PS:最近没有登录博客,很多朋友的留言没有看见,这里道歉!...
  • 1 Object-aduitor审计具体分析 上一篇文章中,讲解了Object-aduitor的启动,其中审计的具体执行是AuditorWorker实现的,在run_audit中实例化了AuditorWorker...def audit_all_objects(self, mode='once', device_dirs
  • swift-init object-replicator start此运行脚本的运行过程和ring运行脚本运行过程差不多,找到swift 源码bin下的swift-object-replicator其代码如下所示 if __name__ == '__main__': parser = OptionParser("%pr
  • Swift启用WSGI服务的事件循环队列pipeline: catch_errors, proxy-logging, cache, authtoken, keystone, (slo), proxy-server。通过直proxy-server的服务入口点进行,调用相关的方法实现请求响应。  proxy-server...
  • 感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正! 如果转载,请保留作者信息。 ... ...PS:最近没有登录博客,很多朋友的留言没有看见,这里道歉!...概述部分:
  • 感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正! 如果转载,请保留作者信息。 ... ...PS:最近没有登录博客,很多朋友的留言没有看见,这里道歉!...接续上一篇博客
  • 本片博文开始以/usr/bin/swift-proxy-server为例,详细分析源码,来进一步解析swift中的服务启动过程; 首先来看swift-proxy-server代码: from swift.common.utils import parse_options from swift.common.wsgi ...
  • 为了记录和巩固学习swift的开源源码,所以进行一系列的源码开源学习笔记,供初学者快速学习和理解swift的内部功能。  proxy下面的server.py模块是所有对account,container,object等对象进行管理操作的在swift的...
  • 分析了服务启动的架构,下面看一下服务启动的源码.分析的不好,还请指教  创建好了builder文件和ring文件之后,下一步的操作就是启动服务了,通常启动单独的服务会有单独的命令,例如swift-proxy-server start等...
  • 为了记录和巩固学习swift的开源源码,所以进行一系列的源码开源学习笔记,供初学者快速学习和理解swift的内部功能。  proxy下面的server.py模块是所有对account,container,object等对象进行管理操作的在swift的...
  • OpenStack Swift client开发

    2014-06-17 10:35:43
    如果你搭建好了Swift环境 ,你应该可以通过Swift 命令 来尝试去测试上传下载等功能,这是因为在安装Swift的时候,通常会安装一个python-swiftclient客户端,这个一个python的开源项目 同样使用apache2.0许可,是...
1 2 3 4 5 ... 15
收藏数 294
精华内容 117