目标:
弄清楚st2rulesengine服务原理
1 总入口
st2/st2reactor/st2reactor/cmd/rulesengine.py
def main():
try:
_setup()
return _run_worker()
except SystemExit as exit_code:
sys.exit(exit_code)
except:
LOG.exception('(PID=%s) RulesEngine quit due to exception.', os.getpid())
return 1
finally:
_teardown()
1.1 进入_setup
def _setup():
common_setup(service='rulesengine', config=config, setup_db=True, register_mq_exchanges=True,
register_signal_handlers=True)
1.2 进入_run_worker
def _run_worker():
LOG.info('(PID=%s) RulesEngine started.', os.getpid())
timer = None
rules_engine_worker = worker.get_worker()
try:
timer_thread = None
if cfg.CONF.timer.enable:
timer = St2Timer(local_timezone=cfg.CONF.timer.local_timezone)
timer_thread = eventlet.spawn(_kickoff_timer, timer)
LOG.info(TIMER_ENABLED_LOG_LINE)
else:
LOG.info(TIMER_DISABLED_LOG_LINE)
rules_engine_worker.start()
if timer:
return timer_thread.wait() and rules_engine_worker.wait()
else:
return rules_engine_worker.wait()
except (KeyboardInterrupt, SystemExit):
LOG.info('(PID=%s) RulesEngine stopped.', os.getpid())
rules_engine_worker.shutdown()
except:
LOG.exception('(PID:%s) RulesEngine quit due to exception.', os.getpid())
return 1
finally:
if timer:
timer.cleanup()
return 0
分析:
1.2.1 分析
rules_engine_worker = worker.get_worker()
进入:
/opt/stackstorm/st2/lib/python2.7/site-packages/st2reactor/rules/worker.py(111)get_worker()
(Pdb) p rules_engine_worker
<st2reactor.rules.worker.TriggerInstanceDispatcher object at 0x2fe3a10>
(Pdb) p rules_engine_worker.__dict__
{'_consumer_thread': None, '_queue_consumer': <st2common.transport.consumers.StagedQueueConsumer object at 0x2fe3f90>, 'rules_engine': <st2reactor.rules.engine.RulesEngine object at 0x1642450>}
(Pdb) p type(rules_engine_worker)
<class 'st2reactor.rules.worker.TriggerInstanceDispatcher'>
分析:
这里的rules_engine_worker实际上是消息处理类,是st2reactor.rules.worker.TriggerInstanceDispatcher对象。
TriggerInstanceDispatcher的基类是
st2/st2common/st2common/transport/consumers.py的MessageHandler类,这个类包含
_queue_consumer对象,这个对象包含处理消息的对象,即MessageHandler类的子类对象,每个继承自
MessageHandler类的子类必须实现process(message)方法用于处理消息。
1.2.2 分析
timer = St2Timer(local_timezone=cfg.CONF.timer.local_timezone)
......
timer_thread = eventlet.spawn(_kickoff_timer, timer)
具体参见 2 的分析
1.2.3 分析
rules_engine_worker = worker.get_worker()
......
rules_engine_worker.start()
具体参见3的分析
2 分析St2Timer类的start方法
timer = St2Timer(local_timezone=cfg.CONF.timer.local_timezone)
......
timer_thread = eventlet.spawn(_kickoff_timer, timer)
其中:
def _kickoff_timer(timer):
timer.start()
2.1 分析
class St2Timer(object):
"""
A timer interface that uses APScheduler 3.0.
"""
def __init__(self, local_timezone=None):
self._timezone = local_timezone
self._scheduler = BlockingScheduler(timezone=self._timezone)
self._jobs = {}
self._trigger_types = TIMER_TRIGGER_TYPES.keys()
self._trigger_watcher = TriggerWatcher(create_handler=self._handle_create_trigger,
update_handler=self._handle_update_trigger,
delete_handler=self._handle_delete_trigger,
trigger_types=self._trigger_types,
queue_suffix=self.__class__.__name__,
exclusive=True)
self._trigger_dispatcher = TriggerDispatcher(LOG)
def start(self):
self._register_timer_trigger_types()
self._trigger_watcher.start()
self._scheduler.start()
2.2)分析
self._register_timer_trigger_types()
进入:
def _register_timer_trigger_types(self):
return trigger_services.add_trigger_models(TIMER_TRIGGER_TYPES.values())
分析:
_register_timer_trigger_types(self)调用add_trigger_models(trigger_types):
2.2.1) 分析
进入: st2/st2common/st2common/services/triggers.py
def add_trigger_models(trigger_types):
"""
Register trigger types.
:param trigger_types: A list of triggers to register.
:type trigger_types: ``list`` of ``dict``
:rtype: ``list`` of ``tuple`` (trigger_type, trigger)
"""
[r for r in (_validate_trigger_type(trigger_type)
for trigger_type in trigger_types) if r is not None]
result = []
for trigger_type in trigger_types:
item = _add_trigger_models(trigger_type=trigger_type)
if item:
result.append(item)
return result
分析:
add_trigger_models(trigger_types):
1 根据输入参数,形如:
(Pdb) p trigger_types
[{'payload_schema': {'type': 'object', 'properties': {'executed_at': {'default': '2014-07-30 05:04:24.578325', 'type': 'string', 'format': 'date-time'}, 'schedule': {'default': {'units': 'seconds', 'delta': 30}, 'type': 'object'}}}, 'description': 'Triggers on specified intervals. e.g. every 30s, 1week etc.', 'parameters_schema': {'additionalProperties': False, 'type': 'object', 'properties': {'timezone': {'type': 'string'}, 'unit': {'enum': ['weeks', 'days', 'hours', 'minutes', 'seconds'], 'required': True}, 'delta': {'required': True, 'type': 'integer'}}}, 'name': 'st2.IntervalTimer', 'pack': 'core'}, {'payload_schema': {'type': 'object', 'properties': {'executed_at': {'default': '2014-07-30 05:04:24.578325', 'type': 'string', 'format': 'date-time'}, 'schedule': {'default': {'units': 'seconds', 'delta': 30}, 'type': 'object'}}}, 'description': 'Triggers exactly once when the current time matches the specified time. e.g. timezone:UTC date:2014-12-31 23:59:59.', 'parameters_schema': {'additionalProperties': False, 'type': 'object', 'properties': {'date': {'required': True, 'type': 'string', 'format': 'date-time'}, 'timezone': {'type': 'string'}}}, 'name': 'st2.DateTimer', 'pack': 'core'}, {'payload_schema': {'type': 'object', 'properties': {'executed_at': {'default': '2014-07-30 05:04:24.578325', 'type': 'string', 'format': 'date-time'}, 'schedule': {'default': {'units': 'seconds', 'delta': 30}, 'type': 'object'}}}, 'description': 'Triggers whenever current time matches the specified time constaints like a UNIX cron scheduler.', 'parameters_schema': {'additionalProperties': False, 'type': 'object', 'properties': {'week': {'minimum': 1, 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'maximum': 53}, 'end_date': {'type': 'string', 'format': 'date-time'}, 'month': {'minimum': 1, 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'maximum': 12}, 'second': {'minimum': 0, 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'maximum': 59}, 'year': {'anyOf': [{'type': 'string'}, {'type': 'integer'}]}, 'timezone': {'type': 'string'}, 'day': {'minimum': 1, 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'maximum': 31}, 'minute': {'minimum': 0, 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'maximum': 59}, 'hour': {'minimum': 0, 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'maximum': 23}, 'day_of_week': {'minimum': 0, 'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'maximum': 6}, 'start_date': {'type': 'string', 'format': 'date-time'}}}, 'name': 'st2.CronTimer', 'pack': 'core'}]
2 遍历trigger_types列表,,每个trigger_type,调用_add_trigger_models(trigger_type)
2.2.2) 分析_add_trigger_models方法
def _add_trigger_models(trigger_type):
pack = trigger_type['pack']
description = trigger_type['description'] if 'description' in trigger_type else ''
payload_schema = trigger_type['payload_schema'] if 'payload_schema' in trigger_type else {}
parameters_schema = trigger_type['parameters_schema'] \
if 'parameters_schema' in trigger_type else {}
tags = trigger_type.get('tags', [])
trigger_type = _create_trigger_type(
pack=pack,
name=trigger_type['name'],
description=description,
payload_schema=payload_schema,
parameters_schema=parameters_schema,
tags=tags
)
trigger = _create_trigger(trigger_type=trigger_type)
return (trigger_type, trigger)
分析:
_add_trigger_models(trigger_type):
1 根据输入参数,形如:
(Pdb) p trigger_type
{'payload_schema': {'type': 'object', 'properties': {'executed_at': {'default': '2014-07-30 05:04:24.578325', 'type': 'string', 'format': 'date-time'}, 'schedule': {'default': {'units': 'seconds', 'delta': 3