启动一个进程,如要想要这个进程的某个方法定时得进行执行的话,在openstack有两种方式: 一种是通过继承 periodic_task.PeriodicTasks,另一种是使用loopingcall.py,针对两种方式分别说一下实现原理。
 
(1) 继承periodic_task.PeriodicTasks
         这种方式比较复杂,用到了python中的一些比较高级的特性,装饰器和元类;首先看一下periodic_task.py,在nova/openstack/common中,其他组件也有。
         看一下PeriodicTasks 这个类。
144 @six.add_metaclass(_PeriodicTasksMeta)
145 class PeriodicTasks(object):
146     def __init__(self):
147         super(PeriodicTasks, self).__init__()
148         self._periodic_last_run = {}
149         for name, task in self._periodic_tasks:
150             self._periodic_last_run[name] = task._periodic_last_run
151
152     def run_periodic_tasks(self, context, raise_on_error=False):
153         """Tasks to be run at a periodic interval."""
154         idle_for = DEFAULT_INTERVAL
155         for task_name, task in self._periodic_tasks:
156             full_task_name = '.'.join([self.__class__.__name__, task_name])
157
158             spacing = self._periodic_spacing[task_name]
159             last_run = self._periodic_last_run[task_name]
160
161             # If a periodic task is _nearly_ due, then we'll run it early
162             if spacing is not None:
163                 idle_for = min(idle_for, spacing)
164                 if last_run is not None:
165                     delta = last_run + spacing - time.time()
166                     if delta > 0.2:
167                         idle_for = min(idle_for, delta)
168                         continue
169
170             LOG.debug("Running periodic task %(full_task_name)s",
171                       {"full_task_name": full_task_name})
172             self._periodic_last_run[task_name] = time.time()
173
174             try:
175                 task(self, context)
176             except Exception as e:
177                 if raise_on_error:
178                     raise
179                 LOG.exception(_LE("Error during %(full_task_name)s: %(e)s"),
180                               {"full_task_name": full_task_name, "e": e})
181             time.sleep(0)
182
183         return idle_for
 
run_periodic_tasks 函数是用户启动各个定时任务的,其中里面有几个数据结构比较重要,self._periodic_tasks:记录来每个task和每个task的函数句柄;self._periodic_spacing: 记录每一个task的运行间隔时间。在__init__函数中,还有构造一个self._periodic_last_run 结构用来记录每一个task上一次运行的时间;具体运行的时候会根据上次运行时间和间隔时间来确定是否运行,函数第162~168行;那具体的self._periodic_tasks和self._periodic_spacing是怎么得来的,是通过元类的方式来实现的;
 
元类可以干预一个类的实现形式,比方说在为一个类添加一个方法,或者为了一个类统一添加某种行为;上文的@six.add_metaclass(_PeriodicTasksMeta)语法就是添加了一个元类,我们看一下_PeriodicTasksMeta的实现;
 
100 class _PeriodicTasksMeta(type):
101     def __init__(cls, names, bases, dict_):
102         """Metaclass that allows us to collect decorated periodic tasks."""
103         super(_PeriodicTasksMeta, cls).__init__(names, bases, dict_)
104
105         # NOTE(sirp): if the attribute is not present then we must be the base
106         # class, so, go ahead an initialize it. If the attribute is present,
107         # then we're a subclass so make a copy of it so we don't step on our
108         # parent's toes.
109         try:
110             cls._periodic_tasks = cls._periodic_tasks[:]
111         except AttributeError:
112             cls._periodic_tasks = []
113
114         try:
115             cls._periodic_spacing = cls._periodic_spacing.copy()
116         except AttributeError:
117             cls._periodic_spacing = {}
118
119         for value in cls.__dict__.values():
120             if getattr(value, '_periodic_task', False):
121                 task = value
122                 name = task.__name__
123
124                 if task._periodic_spacing < 0:
125                     LOG.info(_LI('Skipping periodic task %(task)s because '
126                                  'its interval is negative'),
127                              {'task': name})
128                     continue
129                 if not task._periodic_enabled:
130                     LOG.info(_LI('Skipping periodic task %(task)s because '
131                                  'it is disabled'),
132                              {'task': name})
133                     continue
134
135                 # A periodic spacing of zero indicates that this task should
136                 # be run every pass
137                 if task._periodic_spacing == 0:
138                     task._periodic_spacing = None
139
140                 cls._periodic_tasks.append((name, task))
141                 cls._periodic_spacing[name] = task._periodic_spacing
其中109~117为类添加_periodic_tasks与_periodic_spacing两个类变量, for value in cls.__dict__.values() 语句访问类的各个成员,主要是函数成员;如果发现成员中有_periodic_task属性,并且等于True,则构造_periodic_tasks与_periodic_spacing两个数据结构;那么剩下就要弄清楚task的结构了,task就是类中的一个函数,它为什么具有_periodic_task属性和_periodic_spacing的呢?这个活就是装饰器做的事情了。
 
当你给一个函数设置定时任务的装饰器时,一般会这样写:
@periodic_task.periodic_task(spacing=…, run_immediately=...)
def f(args,kwargs):
…….
 
奥妙就在这个装饰器里面了。
 42 def periodic_task(*args, **kwargs):
 43     """Decorator to indicate that a method is a periodic task.
 44
 45     This decorator can be used in two ways:
 46
 47         1. Without arguments '@periodic_task', this will be run on every cycle
 48            of the periodic scheduler.
 49
 50         2. With arguments:
 51            @periodic_task(spacing=N [, run_immediately=[True|False]])
 52            this will be run on approximately every N seconds. If this number is
 53            negative the periodic task will be disabled. If the run_immediately
 54            argument is provided and has a value of 'True', the first run of the
 55            task will be shortly after task scheduler starts.  If
 56            run_immediately is omitted or set to 'False', the first time the
 57            task runs will be approximately N seconds after the task scheduler
 58            starts.
 59     """
 60     def decorator(f):
 61         # Test for old style invocation
 62         if 'ticks_between_runs' in kwargs:
 63             raise InvalidPeriodicTaskArg(arg='ticks_between_runs')
 64
 65         # Control if run at all
 66         f._periodic_task = True
 67         f._periodic_external_ok = kwargs.pop('external_process_ok', False)
 68         if f._periodic_external_ok and not CONF.run_external_periodic_tasks:
 69             f._periodic_enabled = False
 70         else:
 71             f._periodic_enabled = kwargs.pop('enabled', True)
 72
 73         # Control frequency
 74         f._periodic_spacing = kwargs.pop('spacing', 0)
 75         f._periodic_immediate = kwargs.pop('run_immediately', False)
 76         if f._periodic_immediate:
 77             f._periodic_last_run = None
 78         else:
 79             f._periodic_last_run = time.time()
 80         return f
 81
 82     # NOTE(sirp): The `if` is necessary to allow the decorator to be used with
 83     # and without parents.
 84     #
 85     # In the 'with-parents' case (with kwargs present), this function needs to
 86     # return a decorator function since the interpreter will invoke it like:
 87     #
 88     #   periodic_task(*args, **kwargs)(f)
 89     #
 90     # In the 'without-parents' case, the original function will be passed
 91     # in as the first argument, like:
 92     #
 93     #   periodic_task(f)
 94     if kwargs:
 95         return decorator
 96     else:
 97         return decorator(args[0])
 
在装饰器中,66~80行就是为一个函数设置_periodic_task属性,并从装饰器的kwargs中取得spacing参数,如果有run_immediately(启动之后先立刻执行一次,然后再定时执行)会设置_periodic_last_run属性。这样定时函数运行需要的信息就都齐全了;
 
(2)另一个种方法是使用loopingcall.py ,也在nova/openstack/common中。
使用方法是:obj = loopingcall.FixedIntervalLoopingCall(f, args,kwargs),
                      obj.start(interval,initial_delay)
  代码:
 62 class FixedIntervalLoopingCall(LoopingCallBase):
 63     """A fixed interval looping call."""
 64
 65     def start(self, interval, initial_delay=None):
 66         self._running = True
 67         done = event.Event()
 68
 69         def _inner():
 70             if initial_delay:
 71                 greenthread.sleep(initial_delay)
 72
 73             try:
 74                 while self._running:
 75                     start = timeutils.utcnow()
 76                     self.f(*self.args, **self.kw)
 77                     end = timeutils.utcnow()
 78                     if not self._running:
 79                         break
 80                     delay = interval - timeutils.delta_seconds(start, end)
 81                     if delay <= 0:
 82                         LOG.warn(_LW('task run outlasted interval by %s sec') %
 83                                  -delay)
 84                     greenthread.sleep(delay if delay > 0 else 0)
 85             except LoopingCallDone as e:
 86                 self.stop()
 87                 done.send(e.retvalue)
 88             except Exception:
 89                 LOG.exception(_LE('in fixed duration looping call'))
 90                 done.send_exception(*sys.exc_info())
 91                 return
 92             else:
 93                 done.send(True)
 94
 95         self.done = done
 96
 97         greenthread.spawn_n(_inner)
 98         return self.done
 
start()方法运行定时任务,initial_delay表示是否有延时,75~84每次运行任务要记录开始时间和结束时间,如果开始时间减去结束时间比interval还大的话,那么就不等待了,立刻运行:greenthread.sleep(delay if delay > 0 else 0)

openstack中运行定时任务的两种方法及源代码分析的更多相关文章

  1. python中执行shell的两种方法总结

    这篇文章主要介绍了python中执行shell的两种方法,有两种方法可以在Python中执行SHELL程序,方法一是使用Python的commands包,方法二则是使用subprocess包,这两个包 ...

  2. php获取数组中重复数据的两种方法

    分享下php获取数组中重复数据的两种方法. 1,利用php提供的函数,array_unique和array_diff_assoc来实现 <?php function FetchRepeatMem ...

  3. [转]Qt中定时器使用的两种方法

    Qt中定时器的使用有两种方法,一种是使用QObject类提供的定时器,还有一种就是使用QTimer类. 其精确度一般依赖于操作系统和硬件,但一般支持20ms.下面将分别介绍两种方法来使用定时器. 方法 ...

  4. vue中使用echarts的两种方法

    在vue中使用echarts有两种方法一.第一种方法1.通过npm获取echarts npm install echarts --save 2.在vue项目中引入echarts 在 main.js 中 ...

  5. 在C++中定义常量的两种方法的比较

    常量是定以后,在程序运行中不能被改变的标识符.C++中定义常量可以用#define .const 这两种方法.例如:#define PRICE 10 //定义单价常量10const int PRICE ...

  6. Hive开发中使用变量的两种方法

    在使用hive开发数据分析代码时,经常会遇到需要改变运行参数的情况,比如select语句中对日期字段值的设定,可能不同时间想要看不同日期的数据,这就需要能动态改变日期的值.如果开发量较大.参数多的话, ...

  7. PyQt(Python+Qt)学习随笔:model/view架构中支持QListView列表中展示图标的两种方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 在QListView列表视图中的项不但可以展示文字,也可以展示图标和复选框,同时可以指定项是否可以拖 ...

  8. Ajax中解析Json的两种方法详解

    eval();  //此方法不推荐 JSON.parse();  //推荐方法 一.两种方法的区别 我们先初始化一个json格式的对象: var jsonDate = '{ "name&qu ...

  9. MySQL中删除数据的两种方法

    转自:http://blog.csdn.net/apache6/article/details/2778878 1. 在MySQL中有两种方法可以删除数据: 一种是delete语句,另一种是trunc ...

随机推荐

  1. wordpress内存不足问题“Fatal error:out of memoryin etc...”

    2016年3月22日wordpress用户名及密码登录,出现: “ Fatal error: Out of memory (allocated 19136512) (tried to allocate ...

  2. mysql查询优化(持续更新中)

    1.        索引不会包含有NULL值的列 (1)   应尽量避免在where子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描 (2)   数据库设计时不要让字段的默认值 ...

  3. jquery 设置css样式

       $("#61dh a").css('color', 多个样式属性 var divcss = { background: '#EEE', width: '478px', mar ...

  4. png-24在ie6中的几种透明方法

    转载 http://www.cnblogs.com/jikey/archive/2013/03/13/2957168.html 由于游戏类官网在页面背景和装饰人物的设计上追求画丽且与游戏风格想匹配,这 ...

  5. Eclipse 反编译器

    Help-->Install New SoftWare 贴上反编译地址:http://opensource.cpupk.com/decompiler/update/ 选择add,一路向北,起飞.

  6. C#读取Xml【转】

      XML文件是一种常用的文件格式,例如WinForm里面的app.config以及Web程序中的web.config文件,还有许多重要的场所都有它的身影.Xml是Internet环境中跨平台的,依赖 ...

  7. ExecuteNonQuery()返回值注意点

    在使用ExecuteNonQuery(),调用存储过程,语句执行无错误,但是返回结果一直是-1 原因: 当使用储存过程时, 要把SET NOCOUNT ON  这个语句去掉, 这样数据就有反回值了 当 ...

  8. a标签的css样式四个状态的设计

    表示所有状态下的连接 如 a{color:red} ① a:link:未访问链接 ,如 a:link {color:blue} ② a:visited:已访问链接 ,如 a:visited{color ...

  9. 求x^0+x^1+x^2+.......x^n mod p; x,n,p<=10^9

    方法一:快速幂.但是肯定还是超时. 方法二:利用等比数列公式,但是有除法,做不下去了. 方法三:有点分治的味道.. n为偶数时,x^0+x^1+x^2+.......x^n=(x^0+x^1+x^2+ ...

  10. POJ 1741 树上的点分治

    题目大意: 找到树上点对间距离不大于K的点对数 这是一道简单的练习点分治的题,注意的是为了防止点分治时出现最后分治出来一颗子树为一条直线,所以用递归的方法求出最合适的root点 #include &l ...