nova中定时任务(periodic_task)原理分析

原文地址

https://github.com/stanzgy/wiki/blob/master/openstack/inside-nova-periodic-task.md#nova%E4%B8%AD%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1periodic_task%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90


nova中定时任务(periodic_task)原理分析

在nova源代码中, 可以在很多函数上看到@periodic_task这样的修饰符, 我们知道这是nova的定时任务,可以让这个函数周期性执行, 但是可能不太了解这个修饰符产生作用的原理和用法, 这里将详细说明一下.

decorator nova.manager.periodic_task

    # in nova.manager
    def periodic_task(*args, **kwargs):
        def decorator(f):
            f._periodic_task = True
            f._ticks_between_runs = kwargs.pop('ticks_between_runs', 0)
            return f

        if kwargs:
            return decorator
        else:
            return decorator(args[0])
可以看到 @periodic_task其实只是给被修饰的函数加上了 _periodic_task_ticks_between_runs2个attr, 并没有做其他的操作. 周期执行函数的action实际上是 nova.manager.Managernova.utils.LoopingCall配合实现的, 后面将详细说明.

black magic of nova.manager.ManagerMeta

    # in nova.manager
    class ManagerMeta(type):
        def __init__(cls, names, bases, dict_):
            super(ManagerMeta, cls).__init__(names, bases, dict_)

            try:
                cls._periodic_tasks = cls._periodic_tasks[:]
            except AttributeError:
                cls._periodic_tasks = []

            try:
                cls._ticks_to_skip = cls._ticks_to_skip.copy()
            except AttributeError:
                cls._ticks_to_skip = {}

            for value in cls.__dict__.values():
                if getattr(value, '_periodic_task', False):
                    task = value
                    name = task.__name__
                    cls._periodic_tasks.append((name, task))
                    cls._ticks_to_skip[name] = task._ticks_between_runs
nova.manager中的 ManagerMeta类是 nova.manager.Manager的metaclass(在后面可以看到), 这里可以简单的认为 ManagerMetaManager的父类. 在 nova.manager.Manager初始化时, 会调用 ManagerMeta__init__()方法.

(metaclass属于python里的黑魔法内容, 这里不做详细说明, 大法师们感兴趣可以去看看官方手册http://docs.python.org/reference/datamodel.html#customizing-class-creation)

for value in cls.__dict__.values():
    if getattr(value, '_periodic_task', False):

ManagerMeta.__init__()中的这两行会将cls(也就是Manager对象自己, 注意是Manager对象 不是Manager的实例)中所有使用过@periodic_task修饰符修饰的函数对象 过滤出来.过滤后会将这些函数对象放入Manager._periodic_tasks中, 后面定时任务的实现都是从这个变量里取出函数对象并执行.

nova.manager.Manager.periodic_tasks

(注意和 nova.manager.periodic_task的区别)

    class Manager(base.Base):
        __metaclass__ = ManagerMeta

        def periodic_tasks(self, context, raise_on_error=False):
            for task_name, task in self._periodic_tasks:
                full_task_name = '.'.join([self.__class__.__name__, task_name])

                ticks_to_skip = self._ticks_to_skip[task_name]
                if ticks_to_skip > 0:
                    LOG.debug(_("Skipping %(full_task_name)s, %(ticks_to_skip)s"
                                " ticks left until next run"), locals())
                    self._ticks_to_skip[task_name] -= 1
                    continue

                self._ticks_to_skip[task_name] = task._ticks_between_runs
                LOG.debug(_("Running periodic task %(full_task_name)s"), locals())

                try:
                    task(self, context)
                except Exception as e:
                    if raise_on_error:
                        raise
                    LOG.exception(_("Error during %(full_task_name)s: %(e)s"),
                                  locals())
从前面 ManagerMeta的说明我们已经知道 Manager对象建立时, 会将所有attr _periodic_taskTrue 的函数对象放入 self._periodic_tasks中.

Manager中, 我们可以发现一个和periodic_task十分相似的函数periodic_tasks, 通过阅读函数代码可以发现这个函数实际上的作用就是把所有在self._periodic_tasks中的函数对象(也就是所有用@periodic_task修饰符修饰过的函数)全部遍历并调用一遍. 如果能定期调用这个函数的话, 就能实现类似linux中crontab的定时任务功能.

下面将说明nova如何定时调用nova.manager.Manager.periodic_tasks实现定时任务.

nova.utils.LoopingCall

    class LoopingCall(object):
        def __init__(self, f=None, *args, **kw):
            self.args = args
            self.kw = kw
            self.f = f
            self._running = False

        def start(self, interval, now=True):
            self._running = True
            done = event.Event()

            def _inner():
                if not now:
                    greenthread.sleep(interval)
                try:
                    while self._running:
                        self.f(*self.args, **self.kw)
                        if not self._running:
                            break
                        greenthread.sleep(interval)
                except LoopingCallDone, e:
                    self.stop()
                    done.send(e.retvalue)
                except Exception:
                    LOG.exception(_('in looping call'))
                    done.send_exception(*sys.exc_info())
                    return
                else:
                    done.send(True)

            self.done = done

            greenthread.spawn(_inner)
            return self.done

        def stop(self):
            self._running = False

        def wait(self):
            return self.done.wait()

nova.utils.LoopingCall的作用就是实现前面提到的定时调用函数的功能.

将函数对象作为LoopingCall的第一个构造参数传入构造一个LoopingCall对象, 然后调用其start()方法后调用其wait()方法, 就可以实现定时执行函数的功能.

start方法的interval参数为函数两次执行期间的时间间隔, 单位为秒. 前面提到的@periodic_task修饰符可以设置一个参数ticks_between_runs, 是与其配合使用的, 指经过几次ticks才执行函数. 比如, 上下文为@periodic_task(ticks_between_runs=2)并且interval=5的话, 被修饰的函数将每 (2+1)*5=15 seconds 执行一次

如果被修饰的函数里raise nova.utils.LoopingCallDone, 可以让LoopingCall的定时任务close gracefully.



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值