目標:
弄清楚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代碼