stackstorm 24. 源碼分析之----stackstorm的rulesengine服務分析---4

目標:
弄清楚st2rulesengine服務原理

1和2的分析參見:
https://blog.csdn.net/qingyuanluofeng/article/details/105374863

3的分析參見:
https://blog.csdn.net/qingyuanluofeng/article/details/105374978

4的分析參見:
https://blog.csdn.net/qingyuanluofeng/article/details/105375331


5 st2actionrunner中生產者和消費者分析
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
)


6 st2rulesengine服務總結

6.1 St2Timer
St2Timer中會創建trigger_type,並查詢數據庫中每種trigger類型下的trigger列表,對每個triggger藉助apscheduler觸發定時任務,每次被觸發的trigger叫做trigger instance,
即trigger實例。trigger instance(例如:定時任務)經過層層處理後,最終會調用TriggerInstancePublisher.publish_trigger(self, payload=None, routing_key=None)方法發送消息給st2actionrunner處理。
發送消息,具體是指定exchange類型是topic,名稱是: st2.trigger_instances_dispatch,routing_key是'trigger_instance',payload樣例如下(實際可以認爲payload就是一個
trigger_instance):
{'trace_context': <st2common.models.api.trace.TraceContext object at 0x3be4f90>, 'trigger': {'uid': u'trigger:core:8c13d9a3-12a3-4cff-88d3-66c804a47590:5e0acee19fa878c4fa07d596f7a893d6', 'parameters': {u'unit': u'minutes', u'delta': 5}, 'ref': u'core.8c13d9a3-12a3-4cff-88d3-66c804a47590', 'name': u'8c13d9a3-12a3-4cff-88d3-66c804a47590', 'type': u'core.st2.IntervalTimer', 'id': '5e78a2f0618fd301a43e2284', 'pack': u'core'}, 'payload': {'executed_at': '2020-03-25 10:55:01.848503+00:00', 'schedule': None}}

St2Timer也聚合了TriggerWatcher類,TriggerWatcher類指定隊列爲st2.trigger.watch.St2Timer-<random_string>, exchange類型是topic,名稱是st2.trigger,'routing_key'是 '#'
TriggerWatcher會最終調用apscheduler的BlockingScheduler對象來添加job,job執行_emit_trigger_instance方法並攜帶參數trigger,
_emit_trigger_instance方法發送消息到隊列上,發送消息時使用TriggerDispatcher的dispatch方法。
該dispatch方法發送消息時: exchange是'st2.trigger_instances_dispatch',routing_key是'trigger_instance'。

搜索:
TRIGGER_CUD_XCHG = Exchange('st2.trigger', type='topic')
在st2/st2common/st2common/transport/reactor.py
class TriggerCUDPublisher(publishers.CUDPublisher):
    """
    Publisher responsible for publishing Trigger model CUD events.
    """

    def __init__(self, urls):
        super(TriggerCUDPublisher, self).__init__(urls, TRIGGER_CUD_XCHG)

發現:
st2/st2common/st2common/persistence/trigger.py
class Trigger(ContentPackResource):
    impl = trigger_access
    publisher = None

    @classmethod
    def _get_impl(cls):
        return cls.impl

    @classmethod
    def _get_publisher(cls):
        if not cls.publisher:
            cls.publisher = transport.reactor.TriggerCUDPublisher(
                urls=transport_utils.get_messaging_urls())
        return cls.publisher
也就是說Trigger對象publish時是使用的st2.trigger這個exchange的

topic
主題交換器,工作方式類似於組播,Exchange會將消息轉發和ROUTING_KEY匹配模式相同的所有隊列,比如,ROUTING_KEY爲user.stock的Message會轉發給綁定匹配模式爲 * .stock,user.stock, * . * 和#.user.stock.#的隊列。( * 表是匹配一個任意詞組,#表示匹配0個或多個詞組)

參考:
https://www.jianshu.com/p/19a94c1c729b


6.2 TriggerInstanceDispatcher
TriggerInstanceDispatcher作爲消費者,聚合了規則引擎類RulesEngine。TriggerInstanceDispatcher總入口就是處理消息的process方法,
根據給定的trigger,payload等信息向trigger_d_b表中創建一條trigger_instance記錄(包含: trigger,payload,status,ref等)並返回,然後調用RulesEngine類對象的handle_trigger_instance方法,找到該trigger_instance對應的rules列表,判斷trigger_instance中的payload是否符合criteria要求,從而得到匹配criteria的rules列表。
對每個rule,生成對應的liveaction(包含: status,parameters,runner_info,result等信息)和execution記錄(包含:  action(介紹待執行的action參數,元數據文件,腳本文件等),
runner(介紹執行該action的運行器,模塊,所在包等),liveaction(action名稱,傳入的action執行的參數,運行情況等),result(介紹是否成功/失敗,輸出結果等))
然後進行了如下的消息發送:
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

6.3 服務之間關係
在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。

服務之間的處理路線如下:
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)

6.4 關於定時器
定時器的實現是通過St2Timer類聚合了apscheduler.schedulers.background.BlockingScheduler對象,用於添加定時任務,
其中當添加定時任務時調用 _add_job方法,該方法中指定定時任務執行_emit_trigger_instance方法並傳入該方法參數trigger。
所以本質還是藉助apscheduler實現定時器。


6.5 關於各個實體之間的關係
整體關係如下:
trigger_type
->實例化得到trigger_d_b
->trigger_d_b在參數固定的前提下,每次傳入不同的payload得到不同的trigger_instance
->找到該trigger_instance對應的rules列表,rule中持有這個trigger_d_b的ref,通過trigger的ref可以查詢到對應的rule
->根據rule + trigger_instance
    生成liveaction(action名稱,傳入的action執行的參數,result, context包含trigger_instance_id等),
    生成execution(包含action,runner,liveaction,result)


1) 關於trigger
trigger_type則只指定了payload_schema,parameters_schema等參數的定義。
trigger_d_b是每種類型觸發器trigger_type的實例,trigger_d_b指定了參數最終的值,以及trigger_type。
trigger_instance是包含了payload,status,trigger的type。
關係是:
trigger_type ->實例化得到trigger_d_b -> trigger_d_b在參數固定的前提下,每次傳入不同的payload得到不同的trigger_instance。

trigger_instance中會有trigger的ref信息,以及發生時間,傳遞過來的消息,當前狀態等字段。
可以根據trigger的ref信息找到trigger_d_b表中對應記錄。

2) trigger與rule的關係
rule中會持有這個trigger的ref,所以通過trigger的ref可以查詢到對應的rule。
每一次的觸發rule的信息都寫入數據庫trigger_instance_d_b中,樣例如下:
> db.trigger_instance_d_b.findOne({'_id': ObjectId("5e79d4d58dc0dd00ea0f362f")});
{
    "_id" : ObjectId("5e79d4d58dc0dd00ea0f362f"),
    "trigger" : "core.8c13d9a3-12a3-4cff-88d3-66c804a47590",
    "payload" : {
        "executed_at" : "2020-03-24 09:17:33.887548+00:00",
        "schedule" : null
    },
    "occurrence_time" : ISODate("2020-03-24T09:31:27.463Z"),
    "status" : "processing"
}

3) RuleEnforcer與Rule,TriggerInstance的關係
RuleEnforcer=Rule + TriggerInstance
即一次Rule執行等同於這個Rule加上此次觸發實例的信息
rule_enforcement_d_b中包含trigger_instance_id, execution_id, rule的ref等信息。


4) action與liveaction的關係
liveaction是一次action執行情況,含有此次action執行的具體參數,結果等信息。
當執行action的時候,會獲取action實際參數,context(含trigger_instance, rule等),action名稱,
status等在live_action表中生成一條記錄,樣例如下:
> db.live_action_d_b.find({'_id': ObjectId('5e7ac84b8dc0dd0113f02627')}).pretty();
{
    "_id" : ObjectId("5e7ac84b8dc0dd0113f02627"),
    "status" : "requested",
    "start_timestamp" : NumberLong("1585104727783440"),
    "action" : "email.mistral-network-check",
    "action_is_workflow" : true,
    "parameters" : {
        "cmd" : "curl busybox:80/cmd/network/check.sh%20cn",
        "email_to" : [
            "[email protected]"
        ],
        "email_from" : "[email protected]"
    },
    "result" : {
    },
    "context" : {
        "trigger_instance" : {
            "id" : "5e7abd778dc0dd0113f02625",
            "name" : null
        },
        "trace_context" : {
            "id_" : "5e7abd798dc0dd0113f02626",
            "trace_tag" : "st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590"
        },
        "rule" : {
            "id" : "5e660433c75b50001c06b822",
            "name" : "network-check"
        },
        "user" : "[email protected]"
    },
    "callback" : {
        
    },
    "runner_info" : {
        
    }
}

5) liveaction與actionexecution的關係
真正的執行是用actionexecution,向action_execution_d_b數據庫寫入此次liveaction對應的執行記錄。
得到execution的id後,會更新we_url,所以繼續回寫數據庫。
> db.action_execution_d_b.find({"_id": ObjectId('5e7ad9048dc0dd0113f02628')}).pretty();
{
    "_id" : ObjectId("5e7ad9048dc0dd0113f02628"),
    "trigger" : {
        "uid" : "trigger:core:8c13d9a3-12a3-4cff-88d3-66c804a47590:5e0acee19fa878c4fa07d596f7a893d6",
        "parameters" : {
            "unit" : "minutes",
            "delta" : 5
        },
        "type" : "core.st2.IntervalTimer",
        "pack" : "core",
        "ref" : "core.8c13d9a3-12a3-4cff-88d3-66c804a47590",
        "id" : "5e78a2f0618fd301a43e2284",
        "name" : "8c13d9a3-12a3-4cff-88d3-66c804a47590"
    },
    "trigger_type" : {
        "description" : "Triggers on specified intervals. e.g. every 30s, 1week etc.",
        "tags" : [ ],
        "parameters_schema" : {
            "additionalProperties" : false,
            "type" : "object",
            "properties" : {
                "timezone" : {
                    "type" : "string"
                },
                "unit" : {
                    "enum" : [
                        "weeks",
                        "days",
                        "hours",
                        "minutes",
                        "seconds"
                    ],
                    "required" : true
                },
                "delta" : {
                    "required" : true,
                    "type" : "integer"
                }
            }
        },
        "name" : "st2.IntervalTimer",
        "payload_schema" : {
            "type" : "object",
            "properties" : {
                "executed_at" : {
                    "default" : "2014-07-30 05:04:24.578325",
                    "type" : "string",
                    "format" : "date-time"
                },
                "schedule" : {
                    "default" : {
                        "units" : "seconds",
                        "delta" : 30
                    },
                    "type" : "object"
                }
            }
        },
        "uid" : "trigger_type:core:st2.IntervalTimer",
        "ref" : "core.st2.IntervalTimer",
        "id" : "5e6603b9a7918a0001b8a3b3",
        "pack" : "core"
    },
    "trigger_instance" : {
        "status" : "processing",
        "occurrence_time" : "2020-03-25T02:09:58.990000Z",
        "trigger" : "core.8c13d9a3-12a3-4cff-88d3-66c804a47590",
        "id" : "5e7abd778dc0dd0113f02625",
        "payload" : {
            "executed_at" : "2020-03-25 02:07:30.775464+00:00",
            "schedule" : null
        }
    },
    "rule" : {
        "description" : "Check L3 vRouter & LB & DHCP",
        "tags" : [ ],
        "ref" : "email.network-check",
        "enabled" : true,
        "trigger" : {
            "ref" : "core.8c13d9a3-12a3-4cff-88d3-66c804a47590",
            "type" : "core.st2.IntervalTimer",
            "parameters" : {
                "unit" : "minutes",
                "delta" : 5
            }
        },
        "context" : {
            
        },
        "criteria" : {
            
        },
        "action" : {
            "ref" : "email.mistral-network-check",
            "parameters" : {
                "cmd" : "curl busybox:80/cmd/network/check.sh%20cn",
                "email_to" : [
                    "[email protected]"
                ],
                "email_from" : "[email protected]"
            }
        },
        "uid" : "rule:email:network-check",
        "pack" : "email",
        "type" : {
            "ref" : "standard",
            "parameters" : {
                
            }
        },
        "id" : "5e660433c75b50001c06b822",
        "name" : "network-check"
    },
    "action" : {
        "runner_type" : "mistral-v2",
        "name" : "mistral-network-check",
        "parameters" : {
            "email_account" : {
                "default" : "esdozer",
                "type" : "string"
            },
            "cmd" : {
                "required" : true,
                "type" : "string"
            },
            "email_to" : {
                "required" : true,
                "type" : "array"
            },
            "email_from" : {
                "type" : "string"
            },
            "timeout" : {
                "default" : 1800,
                "type" : "integer"
            }
        },
        "tags" : [ ],
        "enabled" : true,
        "entry_point" : "workflows/mistral-network-check.yaml",
        "notify" : {
            
        },
        "uid" : "action:email:mistral-network-check",
        "pack" : "email",
        "ref" : "email.mistral-network-check",
        "id" : "5e660431c75b50001c06b807",
        "description" : "Run network check script and output the result then send email."
    },
    "runner" : {
        "runner_module" : "mistral_v2",
        "uid" : "runner_type:mistral-v2",
        "name" : "mistral-v2",
        "enabled" : true,
        "query_module" : "mistral_v2",
        "runner_parameters" : {
            "skip_notify" : {
                "default" : [ ],
                "type" : "array",
                "description" : "List of tasks to skip notifications for."
            },
            "task" : {
                "type" : "string",
                "description" : "The name of the task to run for reverse workflow."
            },
            "context" : {
                "default" : {
                    
                },
                "type" : "object",
                "description" : "Additional workflow inputs."
            },
            "workflow" : {
                "type" : "string",
                "description" : "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."
            }
        },
        "id" : "5e6603dcc75b50001c06b2ec",
        "description" : "A runner for executing mistral v2 workflow."
    },
    "liveaction" : {
        "runner_info" : {
            
        },
        "parameters" : {
            "cmd" : "curl busybox:80/cmd/network/check.sh%20cn",
            "email_to" : [
                "[email protected]"
            ],
            "email_from" : "[email protected]"
        },
        "action_is_workflow" : true,
        "callback" : {
            
        },
        "action" : "email.mistral-network-check",
        "id" : "5e7ac84b8dc0dd0113f02627"
    },
    "status" : "requested",
    "start_timestamp" : NumberLong("1585104727783440"),
    "parameters" : {
        "cmd" : "curl busybox:80/cmd/network/check.sh%20cn",
        "email_to" : [
            "[email protected]"
        ],
        "email_from" : "[email protected]"
    },
    "result" : {
        
    },
    "context" : {
        "trigger_instance" : {
            "id" : "5e7abd778dc0dd0113f02625",
            "name" : null
        },
        "trace_context" : {
            "id_" : "5e7abd798dc0dd0113f02626",
            "trace_tag" : "st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590"
        },
        "rule" : {
            "id" : "5e660433c75b50001c06b822",
            "name" : "network-check"
        },
        "user" : "[email protected]"
    },
    "children" : [ ],
    "log" : [
        {
            "status" : "requested",
            "timestamp" : ISODate("2020-03-25T04:04:53.094Z")
        }
    ],
    "web_url" : "https://dozer-st2rulesengine-dfddf8d47-7c8bw/#/history/5e7ad9048dc0dd0113f02628/general"
}

6) 後續處理
liveaction中status從requested變成scheduled的時候,actionrunner服務會調用st2/st2actions/st2actions/worker.py中
消費者處理類ActionExecutionDispatcher(MessageHandler)的_run_action方法,在_run_action方法中,
從liveaction下一步到action_execution
具體是根據liveaction中的id,去action_execution中查詢liveaction__id爲指定id的結果。
db.action_execution_d_b.find({'liveaction.id': '5e7bca79b4a73700014aaa49'}).pretty();
將ActionExecution實際就是action的execution發佈出去的時候,指定
routing_key爲'update',指定exchange類型爲topic,exchange名字爲'st2.execution',

參考:
stackstorm 2.6代碼

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