stackstorm 25. 源码分析之----stackstorm的actionrunner服务分析

目标:
弄清楚actionrunner的原理

1 总入口
st2/st2actions/st2actions/cmd/actionrunner.py

def main():
    try:
        _setup()
        return _run_worker()
    except SystemExit as exit_code:
        sys.exit(exit_code)
    except:
        LOG.exception('(PID=%s) Worker quit due to exception.', os.getpid())
        return 1
    finally:
        _teardown()


def _setup():
    common_setup(service='actionrunner', config=config, setup_db=True, register_mq_exchanges=True,
                 register_signal_handlers=True)
    _setup_sigterm_handler()


def _run_worker():
    LOG.info('(PID=%s) Worker started.', os.getpid())

    components = [
        scheduler.get_scheduler(),
        worker.get_worker()
    ]

    try:
        for component in components:
            component.start()

        for component in components:
            component.wait()
    except (KeyboardInterrupt, SystemExit):
        LOG.info('(PID=%s) Worker stopped.', os.getpid())

        errors = False

        for component in components:
            try:
                component.shutdown()
            except:
                LOG.exception('Unable to shutdown %s.', component.__class__.__name__)
                errors = True

        if errors:
            return 1
    except:
        LOG.exception('(PID=%s) Worker unexpectedly stopped.', os.getpid())
        return 1

    return 0

分析:
1.1)关键代码就是
    components = [
        scheduler.get_scheduler(),
        worker.get_worker()
    ]

    try:
        for component in components:
            component.start()

对于worker.get_worker()及其start()放在2的分析
对于scheduler.get_scheduler()以及其start()放在3的分析


2 worker.get_worker()及其start()
st2/st2actions/st2actions/worker.py

def get_worker():
    with Connection(transport_utils.get_messaging_urls()) as conn:
        return ActionExecutionDispatcher(conn, ACTIONRUNNER_QUEUES)

分析:
2.1) 变量打印
(Pdb) p transport_utils.get_messaging_urls()
['amqp://rabbitmq:[email protected]:5672']
ACTIONRUNNER_QUEUES = [
    queues.ACTIONRUNNER_WORK_QUEUE,
    queues.ACTIONRUNNER_CANCEL_QUEUE,
    queues.ACTIONRUNNER_PAUSE_QUEUE,
    queues.ACTIONRUNNER_RESUME_QUEUE
]

对应具体内容如下:
# Used by the action runner service
ACTIONRUNNER_WORK_QUEUE = liveaction.get_status_management_queue(
    'st2.actionrunner.work', routing_key='scheduled')
ACTIONRUNNER_CANCEL_QUEUE = liveaction.get_status_management_queue(
    'st2.actionrunner.cancel', routing_key='canceling')
ACTIONRUNNER_PAUSE_QUEUE = liveaction.get_status_management_queue(
    'st2.actionrunner.pause', routing_key='pausing')
ACTIONRUNNER_RESUME_QUEUE = liveaction.get_status_management_queue(
    'st2.actionrunner.resume', routing_key='resuming')

def get_status_management_queue(name, routing_key):
    return Queue(name, Exchange('st2.liveaction.status', type='topic'), routing_key=routing_key)

分析:
也就是说ActionExecutionDispatcher作为消息处理类,可以监听处理上面4个队列的消息,
exchange经过routing_key绑定到队列关系如下
'st2.liveaction.status'--->'scheduled'--->'st2.actionrunner.work',
'st2.liveaction.status'--->'canceling'--->'st2.actionrunner.cancel', 
'st2.liveaction.status'--->'pausing'--->'st2.actionrunner.pause',
'st2.liveaction.status'--->'resuming'--->'st2.actionrunner.resume'


2.2) 分析ActionExecutionDispatcher(conn, ACTIONRUNNER_QUEUES)
class ActionExecutionDispatcher(MessageHandler):
    message_type = LiveActionDB

    def __init__(self, connection, queues):
        super(ActionExecutionDispatcher, self).__init__(connection, queues)
        self.container = RunnerContainer()
        self._running_liveactions = set()

2.2.1)打印
(Pdb) p connection
<Connection: amqp://rabbitmq:**@rabbitmq.openstack.svc.cluster.local:5672// at 0x4863090>
(Pdb) p queues
[<unbound Queue st2.actionrunner.work -> <unbound Exchange st2.liveaction.status(topic)> -> scheduled>, <unbound Queue st2.actionrunner.cancel -> <unbound Exchange st2.liveaction.status(topic)> -> canceling>, <unbound Queue st2.actionrunner.pause -> <unbound Exchange st2.liveaction.status(topic)> -> pausing>, <unbound Queue st2.actionrunner.resume -> <unbound Exchange st2.liveaction.status(topic)> -> resuming>]

2.2.2)分析ActionExecutionDispatcher处理入口
ActionExecutionDispatcher本身是消息处理类。

class ActionExecutionDispatcher(MessageHandler):

    def process(self, liveaction):
        """Dispatches the LiveAction to appropriate action runner.

        LiveAction in statuses other than "scheduled" and "canceling" are ignored. If
        LiveAction is already canceled and result is empty, the LiveAction
        is updated with a generic exception message.

        :param liveaction: Action execution request.
        :type liveaction: ``st2common.models.db.liveaction.LiveActionDB``

        :rtype: ``dict``
        """

        if liveaction.status == action_constants.LIVEACTION_STATUS_CANCELED:
            LOG.info('%s is not executing %s (id=%s) with "%s" status.',
                     self.__class__.__name__, type(liveaction), liveaction.id, liveaction.status)
            if not liveaction.result:
                updated_liveaction = action_utils.update_liveaction_status(
                    status=liveaction.status,
                    result={'message': 'Action execution canceled by user.'},
                    liveaction_id=liveaction.id)
                executions.update_execution(updated_liveaction)
            return

        if liveaction.status not in ACTIONRUNNER_DISPATCHABLE_STATES:
            LOG.info('%s is not dispatching %s (id=%s) with "%s" status.',
                     self.__class__.__name__, type(liveaction), liveaction.id, liveaction.status)
            return

        try:
            liveaction_db = action_utils.get_liveaction_by_id(liveaction.id)
        except StackStormDBObjectNotFoundError:
            LOG.exception('Failed to find liveaction %s in the database.', liveaction.id)
            raise

        if liveaction.status != liveaction_db.status:
            LOG.warning(
                'The status of liveaction %s has changed from %s to %s '
                'while in the queue waiting for processing.',
                liveaction.id,
                liveaction.status,
                liveaction_db.status
            )

        dispatchers = {
            action_constants.LIVEACTION_STATUS_SCHEDULED: self._run_action,
            action_constants.LIVEACTION_STATUS_CANCELING: self._cancel_action,
            action_constants.LIVEACTION_STATUS_PAUSING: self._pause_action,
            action_constants.LIVEACTION_STATUS_RESUMING: self._resume_action
        }

        return dispatchers[liveaction.status](liveaction)

分析:
2.2.2.1) 打印变量
(Pdb) p liveaction
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=True, callback={}, context={u'trigger_instance': {u'id': u'5e7b88d8b4a73700014aa920', u'name': None}, u'trace_context': {u'id_': u'5e7b88d8b4a73700014aa921', u'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, u'rule': {u'id': u'5e660433c75b50001c06b822', u'name': u'network-check'}, u'user': u'[email protected]'}, end_timestamp=None, id=5e7b88d9b4a73700014aa922, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'[email protected]'], u'email_from': u'[email protected]'}, result={}, runner_info={}, start_timestamp="2020-03-25 16:37:45.132682+00:00", status="scheduled")>

2.2.2.2)逻辑分析
这里实际上是根据liveaction的不同状态分别调用不同的处理方法,
例如如果状态是scheduled,就会调用ActionExecutionDispatcher类的_run_action方法。
重点分析_run_action方法,具体参见2.3)的分析

2.3) 分析ActionExecutionDispatcher类的_run_action方法
class ActionExecutionDispatcher(MessageHandler):

    def _run_action(self, liveaction_db):
        # stamp liveaction with process_info
        runner_info = system_info.get_process_info()

        # Update liveaction status to "running"
        liveaction_db = action_utils.update_liveaction_status(
            status=action_constants.LIVEACTION_STATUS_RUNNING,
            runner_info=runner_info,
            liveaction_id=liveaction_db.id)

        self._running_liveactions.add(liveaction_db.id)

        action_execution_db = executions.update_execution(liveaction_db)

        # Launch action
        extra = {'action_execution_db': action_execution_db, 'liveaction_db': liveaction_db}
        LOG.audit('Launching action execution.', extra=extra)

        # the extra field will not be shown in non-audit logs so temporarily log at info.
        LOG.info('Dispatched {~}action_execution: %s / {~}live_action: %s with "%s" status.',
                 action_execution_db.id, liveaction_db.id, liveaction_db.status)

        extra = {'liveaction_db': liveaction_db}
        try:
            result = self.container.dispatch(liveaction_db)
            LOG.debug('Runner dispatch produced result: %s', result)
            if not result:
                raise ActionRunnerException('Failed to execute action.')
        except:
            _, ex, tb = sys.exc_info()
            extra['error'] = str(ex)
            LOG.info('Action "%s" failed: %s' % (liveaction_db.action, str(ex)), extra=extra)

            liveaction_db = action_utils.update_liveaction_status(
                status=action_constants.LIVEACTION_STATUS_FAILED,
                liveaction_id=liveaction_db.id,
                result={'error': str(ex), 'traceback': ''.join(traceback.format_tb(tb, 20))})
            executions.update_execution(liveaction_db)
            raise
        finally:
            # In the case of worker shutdown, the items are removed from _running_liveactions.
            # As the subprocesses for action executions are terminated, this finally block
            # will be executed. Set remove will result in KeyError if item no longer exists.
            # Use set discard to not raise the KeyError.
            self._running_liveactions.discard(liveaction_db.id)

        return result

分析:
2.3.1) 打印变量
(Pdb) p liveaction_db
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=True, callback={}, context={u'trigger_instance': {u'id': u'5e7b88d8b4a73700014aa920', u'name': None}, u'trace_context': {u'id_': u'5e7b88d8b4a73700014aa921', u'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, u'rule': {u'id': u'5e660433c75b50001c06b822', u'name': u'network-check'}, u'user': u'[email protected]'}, end_timestamp=None, id=5e7b88d9b4a73700014aa922, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'[email protected]'], u'email_from': u'[email protected]'}, result={}, runner_info={}, start_timestamp="2020-03-25 16:37:45.132682+00:00", status="scheduled")>
(Pdb) p action_constants.LIVEACTION_STATUS_RUNNING
'running'
(Pdb) p runner_info
{'hostname': 'dozer-st2actionrunner-5d5c75d4f4-gb7mr', 'pid': 60}
(Pdb) p liveaction_db.id
ObjectId('5e7b88d9b4a73700014aa922')
2.3.2) 流程分析
    ActionExecutionDispatcher._run_action(self, liveaction_db):
    1 调用update_liveaction_status来更新liveaction的状态
        1 根据输入参数,样例如下:
        status: 'scheduled', result: None, context: None, end_timestamp: None, liveaction_id: ObjectId('5e7b5fd7b4a73700014aa873'), 
        runner_info: None, liveaction_db: None, publish: False
        2 根据liveaction_id查询liveaction,将liveaction的状态从旧的设置成新的,例如从requested修改为scheduled,并更新到数据库,
        3 调用LiveAction.publish_status(liveaction_db)方法,
        指定exchange是topic类型,名字是st2.liveaction.status,routing_key是新的状态(例如scheduled),将当前liveaction_db消息发送出去
    2 根据liveaction_db的id,向action_execution_d_b中查询获取execution记录,
      并利用liveaction_db的信息更新execution记录,返回更新后的execution记录
    3 调用RunnerContainer.dispatch(self, liveaction_db)来执行action,具体是
    4 从已经运行的_running_liveactions集合中移除当前liveaction_db.id
    5 返回liveaction_db结果,形如:{'tasks': []}

2.3.3)重点分析
result = self.container.dispatch(liveaction_db)
进入:
class RunnerContainer(object):

    def dispatch(self, liveaction_db):
        action_db = get_action_by_ref(liveaction_db.action)
        if not action_db:
            raise Exception('Action %s not found in DB.' % (liveaction_db.action))

        liveaction_db.context['pack'] = action_db.pack

        runnertype_db = get_runnertype_by_name(action_db.runner_type['name'])

        extra = {'liveaction_db': liveaction_db, 'runnertype_db': runnertype_db}
        LOG.info('Dispatching Action to a runner', extra=extra)

        # Get runner instance.
        runner = self._get_runner(runnertype_db, action_db, liveaction_db)

        LOG.debug('Runner instance for RunnerType "%s" is: %s', runnertype_db.name, runner)

        # Process the request.
        funcs = {
            action_constants.LIVEACTION_STATUS_REQUESTED: self._do_run,
            action_constants.LIVEACTION_STATUS_SCHEDULED: self._do_run,
            action_constants.LIVEACTION_STATUS_RUNNING: self._do_run,
            action_constants.LIVEACTION_STATUS_CANCELING: self._do_cancel,
            action_constants.LIVEACTION_STATUS_PAUSING: self._do_pause,
            action_constants.LIVEACTION_STATUS_RESUMING: self._do_resume
        }

        if liveaction_db.status not in funcs:
            raise actionrunner.ActionRunnerDispatchError(
                'Action runner is unable to dispatch the liveaction because it is '
                'in an unsupported status of "%s".' % liveaction_db.status
            )

        liveaction_db = funcs[liveaction_db.status](
            runner=runner,
            runnertype_db=runnertype_db,
            action_db=action_db,
            liveaction_db=liveaction_db
        )

        return liveaction_db.result

分析:
RunnerContainer.dispatch(self, liveaction_db):
1 根据输入参数,形如:
(Pdb) p liveaction_db
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=True, callback={}, context={u'trigger_instance': {u'id': u'5e7c4b9294919e0001e7d8b8', u'name': None}, u'trace_context': {u'id_': u'5e7c4b9294919e0001e7d8b9', u'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, u'rule': {u'id': u'5e660433c75b50001c06b822', u'name': u'network-check'}, u'user': u'[email protected]'}, end_timestamp=None, id=5e7c4b9394919e0001e7d8ba, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'[email protected]'], u'email_from': u'[email protected]'}, result={}, runner_info={'hostname': 'dozer-st2actionrunner-5b59dfb6f7-pnfgv', 'pid': 85}, start_timestamp="2020-03-26 06:28:35.132744+00:00", status="running")>
2 根据liveaction_db.action(例如u'email.mistral-network-check')查询到action_db记录
3 根据action_db.runner_type形如({u'name': u'mistral-v2'})查询到runner_db记录
4 获取runner,具体是调用_get_runner(self, runnertype_db, action_db, liveaction_db)根据pack和entry_point进行处理
5 建立<liveaction状态,处理方法>的字典,根据liveaction状态调用对应处理方法。
liveaction状态为scheduler,requested,running都会调用_do_run方法。具体如下:
        

2.3.3.1)分析获取runner,具体是调用_get_runner(self, runnertype_db, action_db, liveaction_db)
    def _get_runner(self, runnertype_db, action_db, liveaction_db):
        resolved_entry_point = self._get_entry_point_abs_path(action_db.pack,
                                                              action_db.entry_point)
        context = getattr(liveaction_db, 'context', dict())
        user = context.get('user', cfg.CONF.system_user.user)

        # Note: Right now configs are only supported by the Python runner actions
        if runnertype_db.runner_module == 'python_runner':
            LOG.debug('Loading config for pack')

            config_loader = ContentPackConfigLoader(pack_name=action_db.pack, user=user)
            config = config_loader.get_config()
        else:
            config = None

        runner = get_runner(module_name=runnertype_db.runner_module, config=config)

        # TODO: Pass those arguments to the constructor instead of late
        # assignment, late assignment is awful
        runner.runner_type_db = runnertype_db
        runner.action = action_db
        runner.action_name = action_db.name
        runner.liveaction = liveaction_db
        runner.liveaction_id = str(liveaction_db.id)
        runner.execution = ActionExecution.get(liveaction__id=runner.liveaction_id)
        runner.execution_id = str(runner.execution.id)
        runner.entry_point = resolved_entry_point
        runner.context = context
        runner.callback = getattr(liveaction_db, 'callback', dict())
        runner.libs_dir_path = self._get_action_libs_abs_path(action_db.pack,
                                                              action_db.entry_point)

        # For re-run, get the ActionExecutionDB in which the re-run is based on.
        rerun_ref_id = runner.context.get('re-run', {}).get('ref')
        runner.rerun_ex_ref = ActionExecution.get(id=rerun_ref_id) if rerun_ref_id else None

        return runner

分析:
_get_runner(self, runnertype_db, action_db, liveaction_db):
1 根据输入参数,形如:
(Pdb) p runnertype_db
<RunnerTypeDB: RunnerTypeDB(description="A runner for executing mistral v2 workflow.", enabled=True, id=5e6603dcc75b50001c06b2ec, name="mistral-v2", query_module="mistral_v2", runner_module="mistral_v2", runner_parameters={u'skip_notify': {u'default': [], u'type': u'array', u'description': u'List of tasks to skip notifications for.'}, u'task': {u'type': u'string', u'description': u'The name of the task to run for reverse workflow.'}, u'context': {u'default': {}, u'type': u'object', u'description': u'Additional workflow inputs.'}, u'workflow': {u'type': u'string', u'description': u'The name of the workflow to run if the entry_point is a workbook of many workflows. The name should be in the format "<pack_name>.<action_name>.<workflow_name>". If entry point is a workflow or a workbook with a single workflow, the runner will identify the workflow automatically.'}}, uid="runner_type:mistral-v2")>
(Pdb) p action_db
<ActionDB: ActionDB(description="Run network check script and output the result then send email.", enabled=True, entry_point="workflows/mistral-network-check.yaml", id=5e660431c75b50001c06b807, name="mistral-network-check", notify=NotifySchema@60239952(on_complete="None", on_success="None", on_failure="None"), pack="email", parameters={u'email_account': {u'default': u'esdozer', u'type': u'string'}, u'cmd': {u'required': True, u'type': u'string'}, u'email_to': {u'required': True, u'type': u'array'}, u'email_from': {u'type': u'string'}, u'timeout': {u'default': 1800, u'type': u'integer'}}, ref="email.mistral-network-check", runner_type={u'name': u'mistral-v2'}, tags=[], uid="action:email:mistral-network-check")>
(Pdb) p liveaction_db
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=True, callback={}, context={u'trigger_instance': {u'id': u'5e7c4cbe94919e0001e7d8cd', u'name': None}, 'pack': u'email', u'trace_context': {u'id_': u'5e7c4cbe94919e0001e7d8ce', u'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, u'rule': {u'id': u'5e660433c75b50001c06b822', u'name': u'network-check'}, u'user': u'[email protected]'}, end_timestamp=None, id=5e7c4cbf94919e0001e7d8cf, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'[email protected]'], u'email_from': u'[email protected]'}, result={}, runner_info={'hostname': 'dozer-st2actionrunner-5b59dfb6f7-pnfgv', 'pid': 102}, start_timestamp="2020-03-26 06:33:35.036242+00:00", status="running")>
2 根据action_db.pack(例如: 'email')和action_db.entry_point(例如: u'workflows/mistral-network-check.yaml')得到真正的entry_point地址
(例如: u'/opt/stackstorm/packs/email/actions/workflows/mistral-network-check.yaml')
3 根据runnertype_db.runner_module(例如: 'mistral_v2')来获取runner(
例如st2/contrib/runners/mistral_v2/mistral_v2/mistral_v2.py的MistralRunner(str(uuid.uuid4())))
并设置该runner的runner_type_db,action_name,liveaction,liveaction_id,
根据liveaction_id向数据库查询对应的execution,并设置runner的execution, execution_id, entry_point等
4 返回该runner,形如:
(Pdb) p runner
<mistral_v2.MistralRunner object at 0x3a90310>

2.3.3.2)分析RunnerContainer的_do_run(self, runner, runnertype_db, action_db, liveaction_db)
class RunnerContainer(object):

    def _do_run(self, runner, runnertype_db, action_db, liveaction_db):
        # Create a temporary auth token which will be available
        # for the duration of the action execution.
        runner.auth_token = self._create_auth_token(context=runner.context, action_db=action_db,
                                                    liveaction_db=liveaction_db)

        try:
            # Finalized parameters are resolved and then rendered. This process could
            # fail. Handle the exception and report the error correctly.
            try:
                runner_params, action_params = param_utils.render_final_params(
                    runnertype_db.runner_parameters, action_db.parameters, liveaction_db.parameters,
                    liveaction_db.context)
                runner.runner_parameters = runner_params
            except ParamException as e:
                raise actionrunner.ActionRunnerException(str(e))

            LOG.debug('Performing pre-run for runner: %s', runner.runner_id)
            runner.pre_run()

            # Mask secret parameters in the log context
            resolved_action_params = ResolvedActionParameters(action_db=action_db,
                                                              runner_type_db=runnertype_db,
                                                              runner_parameters=runner_params,
                                                              action_parameters=action_params)
            extra = {'runner': runner, 'parameters': resolved_action_params}
            LOG.debug('Performing run for runner: %s' % (runner.runner_id), extra=extra)
            (status, result, context) = runner.run(action_params)

            try:
                result = json.loads(result)
            except:
                pass

            action_completed = status in action_constants.LIVEACTION_COMPLETED_STATES
            if isinstance(runner, AsyncActionRunner) and not action_completed:
                self._setup_async_query(liveaction_db.id, runnertype_db, context)
        except:
            LOG.exception('Failed to run action.')
            _, ex, tb = sys.exc_info()
            # mark execution as failed.
            status = action_constants.LIVEACTION_STATUS_FAILED
            # include the error message and traceback to try and provide some hints.
            result = {'error': str(ex), 'traceback': ''.join(traceback.format_tb(tb, 20))}
            context = None
        finally:
            # Log action completion
            extra = {'result': result, 'status': status}
            LOG.debug('Action "%s" completed.' % (action_db.name), extra=extra)

            # Update the final status of liveaction and corresponding action execution.
            liveaction_db = self._update_status(liveaction_db.id, status, result, context)

            # Always clean-up the auth_token
            # This method should be called in the finally block to ensure post_run is not impacted.
            self._clean_up_auth_token(runner=runner, status=status)

        LOG.debug('Performing post_run for runner: %s', runner.runner_id)
        runner.post_run(status=status, result=result)

        LOG.debug('Runner do_run result', extra={'result': liveaction_db.result})
        LOG.audit('Liveaction completed', extra={'liveaction_db': liveaction_db})

        return liveaction_db

分析:
RunnerContainer的_do_run(self, runner, runnertype_db, action_db, liveaction_db):
0 根据输入参数,形如:
1 给runner创建auth_token,计算runner参数
2 调用MistralRunner类的run方法来真正执行action
3  调用 _setup_async_query(self, liveaction_id, runnertype_db, query_context)方法向action_execution_state_d_b
    写入此次工作流执行的execution_id,工作流名称等信息
    具体是:
            调用_create_execution_state(self, liveaction_id, runnertype_db, query_context):
                根据形如如下的参数:
                向action_execution_state_d_b表写入一条动作执行状态记录,返回写入结果,写入后结果样例如下:
                    > db.action_execution_state_d_b.findOne({})
                    {
                        "_id" : ObjectId("5e7d9adb9763c5002f3e52a6"),
                        "execution_id" : ObjectId("5e7d92c81668b30001a3cd60"),
                        "query_module" : "mistral_v2",
                        "query_context" : {
                            "mistral" : {
                                "workflow_name" : "email.mistral-network-check",
                                "execution_id" : "63463d91-60b9-4f70-a36d-5a039291cd7f"
                            }
                        }
                    }
                这里应该就是建立了st2中execution_id到mistral中execution_id的映射关系
4 调用_update_status(self, liveaction_id, status, result, context)来
更新了liveaction和该liveaction对应的action_execution
    总结: 更新了liveaction和该liveaction对应的action_execution
5 返回liveaction
6 返回liveaction_db.result
形如:{'tasks': []}


2.3.3.3) 分析(status, result, context) = runner.run(action_params)
进入st2/contrib/runners/mistral_v2/mistral_v2/mistral_v2.py
class MistralRunner(AsyncActionRunner):

    @retrying.retry(
        retry_on_exception=utils.retry_on_exceptions,
        wait_exponential_multiplier=cfg.CONF.mistral.retry_exp_msec,
        wait_exponential_max=cfg.CONF.mistral.retry_exp_max_msec,
        stop_max_delay=cfg.CONF.mistral.retry_stop_max_msec)
    def run(self, action_parameters):
        resume_options = self._get_resume_options()

        tasks_to_reset = resume_options.get('reset', [])

        task_specs = {
            task_name: {'reset': task_name in tasks_to_reset}
            for task_name in resume_options.get('tasks', [])
        }

        resume = self.rerun_ex_ref and task_specs

        if resume:
            result = self.resume_workflow(ex_ref=self.rerun_ex_ref, task_specs=task_specs)
        else:
            result = self.start_workflow(action_parameters=action_parameters)

        return result

分析:
MistralRunner类的run方法根据action_parameters,形如:
{u'email_account': u'esdozer', u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'[email protected]'], u'email_from': u'[email protected]', u'timeout': 1800}
然后调用MistralRunner的start_workflow方法,具体参见下面的分析

2.3.3.4)分析self.start_workflow(action_parameters=action_parameters)
    def start_workflow(self, action_parameters):
        # Test connection
        self._client.workflows.list()

        # Setup inputs for the workflow execution.
        inputs = self.runner_parameters.get('context', dict())
        inputs.update(action_parameters)

        # Get workbook/workflow definition from file.
        def_yaml = self.get_workflow_definition(self.entry_point)
        def_dict = yaml.safe_load(def_yaml)
        is_workbook = ('workflows' in def_dict)

        if not is_workbook:
            # Non-workbook definition containing multiple workflows is not supported.
            if len([k for k, _ in six.iteritems(def_dict) if k != 'version']) != 1:
                raise Exception('Workflow (not workbook) definition is detected. '
                                'Multiple workflows is not supported.')

        action_ref = '%s.%s' % (self.action.pack, self.action.name)
        self._check_name(action_ref, is_workbook, def_dict)
        def_dict_xformed = utils.transform_definition(def_dict)
        def_yaml_xformed = yaml.safe_dump(def_dict_xformed, default_flow_style=False)

        # Construct additional options for the workflow execution
        options = self._construct_workflow_execution_options()

        # Save workbook/workflow definition.
        if is_workbook:
            self._save_workbook(action_ref, def_yaml_xformed)
            default_workflow = self._find_default_workflow(def_dict_xformed)
            execution = self._client.executions.create(default_workflow,
                                                       workflow_input=inputs,
                                                       **options)
        else:
            self._save_workflow(action_ref, def_yaml_xformed)
            execution = self._client.executions.create(action_ref,
                                                       workflow_input=inputs,
                                                       **options)

        status = action_constants.LIVEACTION_STATUS_RUNNING
        partial_results = {'tasks': []}

        # pylint: disable=no-member
        current_context = {
            'execution_id': str(execution.id),
            'workflow_name': execution.workflow_name
        }

        exec_context = self.context
        exec_context = self._build_mistral_context(exec_context, current_context)
        LOG.info('Mistral query context is %s' % exec_context)

        return (status, partial_results, exec_context)

分析:
MistralRunner的start_workflow方法根据action_parameters,形如:
{u'email_account': u'esdozer', u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'[email protected]'], u'email_from': u'[email protected]', u'timeout': 1800}
1 根据entry_point定义的工作流定义文件,读取该文件内容
2 根据workflow名称去mistral中查询是否存在对应工作流,不存在,
则根据当前提供的工作流定义文件内容去mistral中创建一个工作流
3 根据如下参数action_ref,inputs,options去mistral中创建一个execution
(Pdb) p action_ref
u'email.mistral-network-check'
(Pdb) p inputs
{u'email_account': u'esdozer', u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'[email protected]'], u'email_from': u'[email protected]', u'timeout': 1800}
(Pdb) p options
{'env': {'st2_liveaction_id': '5e7d92c81668b30001a3cd60', 'st2_execution_id': '5e7d92c81668b30001a3cd61', '__actions': {'st2.action': {'st2_context': {'endpoint': 'http://st2api:9101/v1/actionexecutions', 'api_url': 'http://st2api:9101/v1', 'parent': {u'trigger_instance': {u'id': u'5e7d92c81668b30001a3cd5e', u'name': None}, u'rule': {u'id': u'5e78b31d78407b0014a12d18', u'name': u'network-check'}, u'user': u'[email protected]', u'trace_context': {u'id_': u'5e7d92c81668b30001a3cd5f', u'trace_tag': u'st2.IntervalTimer-acc696f3-7e15-4fe3-bcaa-9314d49cdca6'}, 'execution_id': '5e7d92c81668b30001a3cd61', 'pack': u'email'}, 'auth_token': u'c8c97842e0564558bdc09d29b4393163', 'skip_notify_tasks': [], 'notify': {}}}}, 'st2_action_api_url': 'http://st2api:9101/v1'}}
返回execution结果样例如下:
(Pdb) p execution
<mistralclient.api.v2.executions.Execution object at 0x47dccd0>
(Pdb) p execution.__dict__
{'state_info': None, 'description': u'', 'workflow_name': u'email.mistral-network-check', 'state': u'RUNNING', 'updated_at': u'2020-03-27 06:04:22', 'created_at': u'2020-03-27 06:04:22', 'workflow_id': u'b118f7ec-863d-4fa0-ad98-c666598dc362', 'task_execution_id': None, 'manager': <mistralclient.api.v2.executions.ExecutionManager object at 0x47ab290>, 'params': u'{"namespace": "", "env": {"st2_liveaction_id": "5e7d92c81668b30001a3cd60", "st2_execution_id": "5e7d92c81668b30001a3cd61", "__actions": {"st2.action": {"st2_context": {"endpoint": "http://st2api:9101/v1/actionexecutions", "api_url": "http://st2api:9101/v1", "parent": {"trace_context": {"id_": "5e7d92c81668b30001a3cd5f", "trace_tag": "st2.IntervalTimer-acc696f3-7e15-4fe3-bcaa-9314d49cdca6"}, "rule": {"id": "5e78b31d78407b0014a12d18", "name": "network-check"}, "trigger_instance": {"id": "5e7d92c81668b30001a3cd5e", "name": null}, "user": "[email protected]", "execution_id": "5e7d92c81668b30001a3cd61", "pack": "email"}, "auth_token": "c8c97842e0564558bdc09d29b4393163", "notify": {}, "skip_notify_tasks": []}}}, "st2_action_api_url": "http://st2api:9101/v1"}}', 'workflow_namespace': u'', 'output': u'{}', 'input': u'{"email_account": "esdozer", "cmd": "curl busybox:80/cmd/network/check.sh%20cn", "email_to": ["[email protected]"], "email_from": "[email protected]", "timeout": 1800}', '_data': {u'state_info': None, u'description': u'', u'workflow_id': u'b118f7ec-863d-4fa0-ad98-c666598dc362', u'created_at': u'2020-03-27 06:04:22', u'task_execution_id': None, u'updated_at': u'2020-03-27 06:04:22', u'state': u'RUNNING', u'params': u'{"namespace": "", "env": {"st2_liveaction_id": "5e7d92c81668b30001a3cd60", "st2_execution_id": "5e7d92c81668b30001a3cd61", "__actions": {"st2.action": {"st2_context": {"endpoint": "http://st2api:9101/v1/actionexecutions", "api_url": "http://st2api:9101/v1", "parent": {"trace_context": {"id_": "5e7d92c81668b30001a3cd5f", "trace_tag": "st2.IntervalTimer-acc696f3-7e15-4fe3-bcaa-9314d49cdca6"}, "rule": {"id": "5e78b31d78407b0014a12d18", "name": "network-check"}, "trigger_instance": {"id": "5e7d92c81668b30001a3cd5e", "name": null}, "user": "[email protected]", "execution_id": "5e7d92c81668b30001a3cd61", "pack": "email"}, "auth_token": "c8c97842e0564558bdc09d29b4393163", "notify": {}, "skip_notify_tasks": []}}}, "st2_action_api_url": "http://st2api:9101/v1"}}', u'workflow_namespace': u'', u'input': u'{"email_account": "esdozer", "cmd": "curl busybox:80/cmd/network/check.sh%20cn", "email_to": ["[email protected]"], "email_from": "[email protected]", "timeout": 1800}', u'id': u'63463d91-60b9-4f70-a36d-5a039291cd7f', u'output': u'{}', u'project_id': u'<default-project>', u'workflow_name': u'email.mistral-network-check'}, 'project_id': u'<default-project>', 'id': u'63463d91-60b9-4f70-a36d-5a039291cd7f'}
4 最终start_workflow返回一个三元组,样例如下:
(Pdb) p (status, partial_results, exec_context)
('running', {'tasks': []}, {'mistral': {'workflow_name': u'email.mistral-network-check', 'execution_id': '63463d91-60b9-4f70-a36d-5a039291cd7f'}})

================================
总结:
st2-actionrunner服务的主处理类是ActionExecutionDispatcher(MessageHandler),这个类本身是一个消息处理类,
该类聚合了RunnerContainer类用于执行action,ActionExecutionDispatcher包含一个处理消息的总入口方法process。
在process(self, liveaction)中处理的简要流程如下:
根据liveaction的id向数据库查询到对应liveaction记录,更新liveaction的状态,指定exchange是topic类型,名字是st2.liveaction.status,routing_key是新的状态(例如scheduled),将当前liveaction_db消息发送出去。
根据liveaction_db的id,向action_execution_d_b中查询获取execution记录,并利用liveaction_db的信息更新execution记录,
获取runner,不同的runner执行自己的run方法。
以Mistral的Runner为例:
runner根据action_parameters,在MistralRunner中根据entry_point定义的工作流定义文件,读取该文件内容并在mistral中创建工作流,
并根据action_ref,inputs(实际是参数字典),options去mistral中创建一个execution,向action_execution_state_d_b表写入一条动作执行状态记录。
更新liveaction和该liveaction对应的action_execution,返回liveaction的result。

具体流程如下:
1 根据输入参数,形如:
(Pdb) p liveaction
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=True, callback={}, context={u'trigger_instance': {u'id': u'5e7b88d8b4a73700014aa920', u'name': None}, u'trace_context': {u'id_': u'5e7b88d8b4a73700014aa921', u'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, u'rule': {u'id': u'5e660433c75b50001c06b822', u'name': u'network-check'}, u'user': u'[email protected]'}, end_timestamp=None, id=5e7b88d9b4a73700014aa922, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'[email protected]'], u'email_from': u'[email protected]'}, result={}, runner_info={}, start_timestamp="2020-03-25 16:37:45.132682+00:00", status="scheduled")>
2 根据liveaction的id向数据库查询到对应liveaction记录
3 建立<liveaction状态,处理方法>的字典,其中状态为'scheduled'对应的处理方法为_run_action
在_run_action方法中调用ActionExecutionDispatcher._run_action(self, liveaction_db)。具体是
    1 调用update_liveaction_status来更新liveaction的状态,例如从requested修改为scheduled,并更新到数据库,
      指定exchange是topic类型,名字是st2.liveaction.status,routing_key是新的状态(例如scheduled),将当前liveaction_db消息发送出去
    2 根据liveaction_db的id,向action_execution_d_b中查询获取execution记录,
      并利用liveaction_db的信息更新execution记录,返回更新后的execution记录
    3 调用RunnerContainer.dispatch(self, liveaction_db)来执行action,具体是根据liveaction_db.action查询到action_db记录,根据action_db.runner_type查询到runner_db记录,获取runner,根据action_db.pack和action_db.entry_point得到真正的entry_point地址,根据runnertype_db.runner_module来获取runner,并设置该runner的runner_type_db,action_name,liveaction,liveaction_id,根据liveaction_id向数据库查询对应的execution,并设置runner的execution, execution_id, entry_point等
   liveaction状态为scheduler,requested,running都会调用_do_run方法。具体如下:
             调用 RunnerContainer的_do_run(self, runner, runnertype_db, action_db, liveaction_db)
             然后调用Runner(例如MistralRunner)类的run方法。MistralRunner类的run方法根据action_parameters,然后调用MistralRunner的start_workflow方法,该方法根据action_parameters,根据entry_point定义的工作流定义文件,读取该文件内容。根据workflow名称去mistral中查询是否存在对应工作流,不存在,则根据当前提供的工作流定义文件内容去mistral中创建一个工作流,并根据参数action_ref,inputs,options去mistral中创建一个execution。最后写入此次工作流执行的execution_id,工作流名称等信息,向action_execution_state_d_b表写入一条动作执行状态记录,返回写入结果。这里应该就是建立了st2中execution_id到mistral中execution_id的映射关系。
        更新了liveaction和该liveaction对应的action_execution
    4 返回liveaction_db结果,形如:{'tasks': []}
=============================================

3 对于scheduler.get_scheduler()以及其start()的分析
总入口:
st2/st2actions/st2actions/scheduler.py

def get_scheduler():
    with Connection(transport_utils.get_messaging_urls()) as conn:
        return ActionExecutionScheduler(conn, [ACTIONSCHEDULER_REQUEST_QUEUE])

3.1) 分析
ACTIONSCHEDULER_REQUEST_QUEUE
队列名称: 'st2.actionrunner.req'
routing_key: 'requested'
exchange类型: topic, 名称: st2.liveaction.status
ActionExecutionScheduler类中监听的队列,绑定关系如下exchange经过绑定key到队列的对应关系是
'st2.liveaction.status'--->'requested'--->'st2.actionrunner.req'

而之前ActionExecutionDispatcher类中监听的队列,绑定关系如下
'st2.liveaction.status'--->'scheduled'--->'st2.actionrunner.work',
'st2.liveaction.status'--->'canceling'--->'st2.actionrunner.cancel', 
'st2.liveaction.status'--->'pausing'--->'st2.actionrunner.pause',
'st2.liveaction.status'--->'resuming'--->'st2.actionrunner.resume'

可见ActionExecutionScheduler和ActionExecutionDispatcher作为消费者采用的都是同一个exchange
'st2.liveaction.status',只是根据liveaction的状态不同得到不同的路由健,然后分发给不同st2.actionrunner.xxx
队列,而liveaction本质上是一个action的实例(包含该action具体的参数)。一个liveaction对应了一个execution,如果
采用mistral作为runnner,则st2中execution关联了mistral中的execution。

3.2)ActionExecutionScheduler实际是一个消费者
class ActionExecutionScheduler(consumers.MessageHandler):
    message_type = LiveActionDB

    def process(self, request):
        """Schedules the LiveAction and publishes the request
        to the appropriate action runner(s).

        LiveAction in statuses other than "requested" are ignored.

        :param request: Action execution request.
        :type request: ``st2common.models.db.liveaction.LiveActionDB``
        """

        if request.status != action_constants.LIVEACTION_STATUS_REQUESTED:
            LOG.info('%s is ignoring %s (id=%s) with "%s" status.',
                     self.__class__.__name__, type(request), request.id, request.status)
            return

        try:
            liveaction_db = action_utils.get_liveaction_by_id(request.id)
        except StackStormDBObjectNotFoundError:
            LOG.exception('Failed to find liveaction %s in the database.', request.id)
            raise

        # Apply policies defined for the action.
        liveaction_db = self._apply_pre_run_policies(liveaction_db=liveaction_db)

        # Exit if the status of the request is no longer runnable.
        # The status could have be changed by one of the policies.
        if liveaction_db.status not in [action_constants.LIVEACTION_STATUS_REQUESTED,
                                        action_constants.LIVEACTION_STATUS_SCHEDULED]:
            LOG.info('%s is ignoring %s (id=%s) with "%s" status after policies are applied.',
                     self.__class__.__name__, type(request), request.id, liveaction_db.status)
            return

        # Update liveaction status to "scheduled".
        if liveaction_db.status == action_constants.LIVEACTION_STATUS_REQUESTED:
            liveaction_db = action_service.update_status(
                liveaction_db, action_constants.LIVEACTION_STATUS_SCHEDULED, publish=False)

        # Publish the "scheduled" status here manually. Otherwise, there could be a
        # race condition with the update of the action_execution_db if the execution
        # of the liveaction completes first.
        LiveAction.publish_status(liveaction_db)

分析:
3.2.1) 
process(self, request): 调度LiveAction并发布request到合适的action runner
1 根据输入参数,形如:
(Pdb) p request
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=True, callback={}, context={'trigger_instance': {'id': '5e7b4d0c15effc00807cbda3', 'name': None}, 'trace_context': {'id_': '5e7b4d0c15effc00807cbda4', 'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, 'rule': {'id': '5e660433c75b50001c06b822', 'name': u'network-check'}, 'user': '[email protected]'}, end_timestamp=None, id=5e7b4d0c15effc00807cbda5, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'[email protected]'], u'email_from': u'[email protected]'}, result={}, runner_info={}, start_timestamp="2020-03-25 12:22:36.822934+00:00", status="requested")>
2 根据liveaction_db的id查询liveaction,如果liveaction的状态为'requested',则更新为'scheduled'。
3 根据liveaction_db的id,向action_execution_d_b中查询获取execution记录,并利用liveaction_db的信息更新execution记录,返回更新后的execution记录
4 根据exchange是topic类型的st2.liveaction.status,routing_key是'scheduled',将payload(实际就是LiveActionDB)发送出去


4 生产者和消费者分析
注意:
LiveAction.publish_status(liveaction_db)
这个方法根据exchange是topic类型的st2.liveaction.status,routing_key是'scheduled',将payload(实际就是LiveActionDB)发送出去
根据之前的分析
ActionExecutionScheduler类中监听的队列,绑定关系如下
'st2.liveaction.status'--->'requested'--->'st2.actionrunner.req'

现在这里发送的消息
exchange是'st2.liveaction.status',routing_key是'scheduled'

ActionExecutionDispatcher类中监听的队列,绑定关系如下
'st2.liveaction.status'--->'scheduled'--->'st2.actionrunner.work',
'st2.liveaction.status'--->'canceling'--->'st2.actionrunner.cancel', 
'st2.liveaction.status'--->'pausing'--->'st2.actionrunner.pause',
'st2.liveaction.status'--->'resuming'--->'st2.actionrunner.resume'

所以ActionExecutionScheduler实际起到的作用就是将liveaction的状态从requested转换为scheduled,
并以exchange是'st2.liveaction.status',routing_key是'scheduled'将liveaction作为payload发送消息出去,
该发送的消息会被ActionExecutionDispatcher类中监听的队列:
'st2.liveaction.status'--->'scheduled'--->'st2.actionrunner.work',
收到,收到消息后,最终会触发action的执行。

而之前在st2actionrunner服务中
TriggerWatcher监听的队列绑定关系如下:
'st2.trigger'--->'#'--->st2.trigger.watch.St2Timer-<random_string>

TriggerInstancePublisher发送的消息是:
exchange: st2.trigger_instances_dispatch, routing_key是'trigger_instance',payload是trigger_instance

TriggerInstanceDispatcher发送的消息是:
liveaction的publish_create发送消息,指定exchange类型是topic,名字是st2.liveaction,routing_key为'create',payload,payload是LiveActionDB对象
liveaction的publish_status发送消息,指定exchange类型是topic,名字是st2.liveaction.status,routing_key是'requested',payload是LiveActionDB对象
actionexecution的publish_create发送消息,指定exchange类型是topic,名字是st2.execution,routing_key是'create',payload是ActionExecutionDB对象

综合分析st2rulesengine和st2actionrunner服务的分析发现
st2rulesengine: TriggerInstanceDispatcher中liveaction的publish_status发送消息时,指定的exchange是'st2.liveaction.status',而且routing_key是'requested'
st2actionrunner: ActionExecutionScheduler和ActionExecutionDispatcher监听队列锁绑定的exchange也都是'st2.liveaction.status',
            其中ActionExecutionScheduler监听队列绑定关系是: 'st2.liveaction.status'--->'requested'--->'st2.actionrunner.req',处理完消息后,
            ActionExecutionScheduler又发送了消息,该消息exchange是'st2.liveaction.status',routing_key是'scheduled'
            而ActionExecutionDispatcher监听队列绑定关系是:  'st2.liveaction.status'--->'scheduled'--->'st2.actionrunner.work',
所以可以分析出服务之间的处理路线如下:
st2rulesengine服务的TriggerInstanceDispatcher发送消息->
st2actionrunner服务的ActionExecutionScheduler监听该队列并处理该消息,然后发送消息->
st2actionrunner服务的ActionExecutionDispatcher监听队列并处理该消息(处理消息实际就是执行action得到execution)

exchange作为一个中转站实现了生产者和队列直接的耦合,因为发送消息是根据不同的需求来的,可能是点对点,可能是根据匹配规则,可能是群发,如果让消息发送者来做处理,会很麻烦,因此,使用exchange来处理各种需求,发送者就发送消息就可以了
参考:
http://www.imooc.com/qadetail/324797

生产者在向外发送消息时,指定了exchange(交换机)和routing key
消费者在消费消息时,需要声明队列(队列名字随便),并将声明的队列通过routing key绑定到exchange,这样才能接收到数据
所以确定生产者和消费者配对需要生产者和消费者是同一个exchange,routing_key符合要求
参考:
https://www.cnblogs.com/tjudzj/p/9250440.html

st2/st2common/st2common/transport/reactor.py
# Exchange for Trigger CUD events
TRIGGER_CUD_XCHG = Exchange('st2.trigger', type='topic')

# Exchange for TriggerInstance events
TRIGGER_INSTANCE_XCHG = Exchange('st2.trigger_instances_dispatch', type='topic')
搜索:
TRIGGER_INSTANCE_XCHG
发现
def get_trigger_instances_queue(name, routing_key):
    return Queue(name, TRIGGER_INSTANCE_XCHG, routing_key=routing_key)

# Used by the rules engine service
RULESENGINE_WORK_QUEUE = reactor.get_trigger_instances_queue(
    name='st2.trigger_instances_dispatch.rules_engine', routing_key='#')

消费者:
def get_worker():
    with Connection(transport_utils.get_messaging_urls()) as conn:
        return TriggerInstanceDispatcher(conn, [RULESENGINE_WORK_QUEUE])

生产者:
class TriggerInstancePublisher(object):
    def __init__(self, urls):
        self._publisher = publishers.PoolPublisher(urls=urls)

    def publish_trigger(self, payload=None, routing_key=None):
        # TODO: We should use trigger reference as a routing key
        self._publisher.publish(payload, TRIGGER_INSTANCE_XCHG, routing_key)

发送消息时:
exchange时st2.trigger_instances_dispatch,routing_key是'trigger_instance'

class TriggerDispatcher(object):
    """
    This trigger dispatcher dispatches trigger instances to a message queue (RabbitMQ).
    """

    def __init__(self, logger=LOG):
        self._publisher = TriggerInstancePublisher(urls=transport_utils.get_messaging_urls())
        self._logger = logger

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)

分析:
TriggerInstanceDispatcher监听RULESENGINE_WORK_QUEUE队列,该队列绑定关系是:
st2.trigger_instances_dispatch--->#--->st2.trigger_instances_dispatch.rules_engine
注意#表示匹配0个或一个词组,这里实际就是fanout,也就是发送给st2.trigger_instances_dispatch这个exchange消息都会发送给
st2.trigger_instances_dispatch.rules_engine队列,然后被TriggerInstanceDispatcher类处理
而发送者是:
St2Timer聚合TriggerDispatcher,而TriggerDispatcher聚合TriggerInstancePublisher,然后
TriggerInstancePublisher中发送消息时指定:
exchange是: 'st2.trigger_instances_dispatch', routing_key是: 'trigger_instance', payload是trigger_instance(含有trace_context, trigger, payload)
综上:
在st2rulesengine服务中,St2Timer聚合的TriggerDispatcher聚合了TriggerInstancePublisher发送消息到名为'st2.trigger_instances_dispatch'的exchange,routing_key是
'trigger_instance',payload是trigger_instance(含有trace_context, trigger, payload)
随后st2rulesengine服务中的TriggerInstanceDispatcher监听如下队列,该队列绑定关系为:
st2.trigger_instances_dispatch--->#--->st2.trigger_instances_dispatch.rules_engine
来处理上述消息。
St2Timer是生产者,TriggerInstanceDispatcher是消费者,服务处理路线为:
st2rulesengine服务的St2Timer聚合TriggerInstancePublisher发送trigger_instance消息 ->
st2rulesengine服务的TriggerInstanceDispatcher监听对应队列st2.trigger_instances_dispatch.rules_engine来处理该trigger_instance消息(处理消息实际就是获取trigger_instance中符合的rules列表,对每个rule,生成对应的liveaction和execution记录
liveaction的publish_create发送消息,指定exchange类型是topic,名字是st2.liveaction,routing_key为'create',payload,payload是LiveActionDB对象
liveaction的publish_status发送消息,指定exchange类型是topic,名字是st2.liveaction.status,routing_key是'requested',payload是LiveActionDB对象
actionexecution的publish_create发送消息,指定exchange类型是topic,名字是st2.execution,routing_key是'create',payload是ActionExecutionDB对象
最后更新enforcement_db的execution_id为execution对象的id,相当于做了关联,然后更新rule_enforcement
)

5 总结
1) 在st2actionrunner服务中两个消费者服务ActionExecutionScheduler和ActionExecutionDispatcher,
其中ActionExecutionScheduler服务既是消费者,也作为生产者发送消息给ActionExecutionDispatcher处理,
ActionExecutionDispatcher接收到消息调用runner去执行action,生成execution,最终完成整个处理过程。
如果采用mistral runner则会建立workflow和execution, st2会关联mistral中的execution到st2本身的execution,
用于追溯execution的执行情况。
2)在st2rulesengine服务中有两个服务St2Timer和TriggerInstanceDispatcher服务,
其中St2Timer既是消费者,也作为生产者发送消息给TriggerInstanceDispatcher处理。
TriggerInstanceDispatcher接收到消息根据trigger_instance获取匹配的rules列表,并对每个rule生成liveaction和execution记录,
liveaction的publish_create发送消息,指定exchange类型是topic,名字是st2.liveaction,routing_key为'create',payload,payload是LiveActionDB对象
liveaction的publish_status发送消息,指定exchange类型是topic,名字是st2.liveaction.status,routing_key是'requested',payload是LiveActionDB对象
actionexecution的publish_create发送消息,指定exchange类型是topic,名字是st2.execution,routing_key是'create',payload是ActionExecutionDB对象
最后更新enforcement_db的execution_id为execution对象的id,相当于做了关联,然后更新rule_enforcement。

3)服务之间的处理路线如下:
st2rulesengine服务的St2Timer聚合TriggerInstancePublisher发送trigger_instance消息->
st2rulesengine服务的TriggerInstanceDispatcher监听st2.trigger_instances_dispatch.rules_engine队列来处理trigger_instance消息,发送liveaction状态修改的消息,当前状态为: 'requested'    --->
st2actionrunner服务的ActionExecutionScheduler监听st2.actionrunner.req队列并处理该消息,状态变更: 'requested'变为'scheduled',发送消息 --->
st2actionrunner服务的ActionExecutionDispatcher监听st2.actionrunner.work队列并处理该消息(处理消息实际就是执行action得到execution)


参考:
stackstorm 2.6代码

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章