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代碼

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