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

来源:互联网 发布:黑马程序员视频百度云 编辑:程序博客网 时间:2024/06/06 09:28

原文地址

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.



原创粉丝点击