Apache DolphinScheduler介紹
Apache DolphinScheduler 是一個分散式易擴充套件的視覺化DAG工作流任務排程開源系統。適用於企業級場景,提供了一個視覺化操作任務、工作流和全生命週期資料處理過程的解決方案。
Dag背景知識
摘錄了一下Dag的offical定義
A graph is formed by vertices and by edges connecting pairs of vertices, where the
vertices can be any kind of object that is connected in pairs by edges.
In the case of a directed graph, each edge has an orientation, from one vertex to another vertex. A path in a directed graph is a sequence of edges having the property that the ending vertex of each
edge in the sequence is the same as the starting vertex of the next edge in the sequence; a path forms a cycle if the starting vertex of its first edge equals the ending vertex of its last edge.
A directed acyclic graph is a directed graph that has no cycles.[1][2][3]
A vertex v of a directed graph is said to be reachable from another
vertex u when there exists a path that starts at u and ends at v.
As a special case, every vertex is considered to be reachable from itself (by a path with zero edges). If a vertex can reach itself via a nontrivial path (a path with one or more edges), then that path is a cycle, so another way to define directed acyclic graphs is that they are the graphs in which no vertex can reach itself via a nontrivial path.
在offical的定義中,有兩物件的集合,集合中的元素是
- vertex
一個實體或者元素,可以是任何抽象的object - edge
一條有方向直線,包含兩個vertex,分別扮演起點和終點
- Dag約束
- 在Dag中,一個edge(a,b)的終點可以作為另一個edge(b,c)的起點,這個鏈路中所有的vertex都是可到達的, c是從a可達的。
- 在Dag中允許vertex不存在於任何一個edge中,這個節點可以從自己到達自己(一個孤島,不和其他vertex有任何聯絡)
- 如果一個vertex可以從自己到達自己,但是中間經過了其他的vertex,那麼這就存在一個環circle
- 在Dag中沒有環
在DolphinScheduler中表示Dag的資料結構為
public class DAG<Node, NodeInfo, EdgeInfo> {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
/**
* node map, key is node, value is node information
*/
private final Map<Node, NodeInfo> nodesMap;
/**
* edge map. key is node of origin;value is Map with key for destination node and value for edge
*/
private final Map<Node, Map<Node, EdgeInfo>> edgesMap;
/**
* reversed edge set,key is node of destination, value is Map with key for origin node and value for edge
*/
private final Map<Node, Map<Node, EdgeInfo>> reverseEdgesMap;
}
其中
- Node表示任務的id
- NodeInfo表示任務的詳細資訊
- EdgeInfo包含任務id和依賴任務id
數倉建設任務和任務依賴
在企業數倉建設中,普遍的做法是進行資料分層(引用https://juejin.cn/post/6969874734355841031)
在生產環境,由於分層的需要,業務邏輯分佈廣泛,資料儲存型別多樣,這就造成了數倉建設的任務多,任務之間依賴複雜,dag就成了最佳的任務依賴和排程的儲存結構。在Dag結構中每個節點表示一個具體的排程任務,任務之間的連線表示依賴關係,針對Dag結構化資料的遍歷過程,就是對數倉任務的執行過程。
一個簡單的數倉依賴任務關係(數倉建設中會有很多工依賴關係和更復雜的任務依賴關係)
DolphinScheduler系統角色拆分
Apache DolphinScheduler核心角色包括MasterServer和WorkerServer,這遵循模組化設計,master和worker專注於自己本身的角色和任務,模組遵循高內聚低耦合的設計,大大提高了系統的穩定性和可擴充套件性,同時也有利於並行開發,縮短系統的研發時間,提高系統的健壯性。
MasterServer主要負責 DAG 任務切分、任務提交監控,並同時監聽其它MasterServer和WorkerServer的健康狀態。 MasterServer服務啟動時向Zookeeper註冊臨時節點,透過監聽Zookeeper臨時節點變化來進行容錯處理。
WorkerServer主要負責任務的執行和提供日誌服務。 WorkerServer服務啟動時向Zookeeper註冊臨時節點,並維持心跳。
DolphinScheduler任務排程流程
參考官網,DolphinScheduler核心任務任務執行流程如下
鑑於任務排程的複雜性,一個大的流程可以劃分為小的流程,在主線流程之外還附加了支線流程,下面對執行排程流程拆分進行分析一下,這樣更容易理解。
Command分發流程
處理方式
非同步,分散式master server節點。
生產者
api-server將使用者的執行工作流http請求封裝成command資料,insert到t_ds_command表中
一個啟動工作流例項的command樣例
{
"commandType": "START_PROCESS",
"processDefinitionCode": 14285512555584,
"executorId": 1,
"commandParam": "{}",
"taskDependType": "TASK_POST",
"failureStrategy": "CONTINUE",
"warningType": "NONE",
"startTime": 1723444881372,
"processInstancePriority": "MEDIUM",
"updateTime": 1723444881372,
"workerGroup": "default",
"tenantCode": "default",
"environmentCode": -1,
"dryRun": 0,
"processInstanceId": 0,
"processDefinitionVersion": 1,
"testFlag": 0
}
消費者
master server中的MasterSchedulerBootstrap loop程式, MasterSchedulerBootstrap使用zk分配到自己的slot,從t_ds_command表中select屬於slot的command列表處理
查詢語句
<select id="queryCommandPageBySlot" resultType="org.apache.dolphinscheduler.dao.entity.Command">
select *
from t_ds_command
where id % #{masterCount} = #{thisMasterSlot}
order by process_instance_priority, id asc
limit #{limit}
</select>
MasterSchedulerBootstrap loop
輪訓查到待處理的command任務,將command任務和master host生成ProcessInstance
,將ProcessInstance
物件插入到t_ds_process_instance
表中,
同時生成包含執行所需要的上下文資訊的可執行任務workflowExecuteRunnable
。
將workflowExecuteRunnable
cache到本地cache processInstanceExecCacheManager
,同時生產將ProcessInstance
的WorkflowEventType.START_WORKFLOW
生產到workflowEventQueue
佇列中。
Dag遍歷執行任務
Master本地cache緩衝
cache實現ProcessInstanceExecCacheManagerImpl
,提供如下核心功能
public interface ProcessInstanceExecCacheManager {
/**
* get WorkflowExecuteThread by process instance id
*
* @param processInstanceId processInstanceId
* @return WorkflowExecuteThread
*/
WorkflowExecuteRunnable getByProcessInstanceId(int processInstanceId);
/**
* judge the process instance does it exist
*
* @param processInstanceId processInstanceId
* @return true - if process instance id exists in cache
*/
boolean contains(int processInstanceId);
/**
* remove cache by process instance id
*
* @param processInstanceId processInstanceId
*/
void removeByProcessInstanceId(int processInstanceId);
/**
* cache
*
* @param processInstanceId processInstanceId
* @param workflowExecuteThread if it is null, will not be cached
*/
void cache(int processInstanceId, @NonNull WorkflowExecuteRunnable workflowExecuteThread);
/**
* get all WorkflowExecuteThread from cache
*
* @return all WorkflowExecuteThread in cache
*/
Collection<WorkflowExecuteRunnable> getAll();
void clearCache();
}
生產者
MasterSchedulerBootstrap loop
將command transform to可以執行的任務,任務物件中包含了要處理的所有上下文資訊
消費者
EventExecuteService
根據dag資訊,拿到第一批沒有任何依賴的TaskInstance
新增到待執行任務佇列standByTaskInstancePriorityQueue
中, standByTaskInstancePriorityQueue
按照優先順序先後順序執行,處理任務狀態,將待執行任務提交到globalTaskDispatchWaitingQueue
佇列中。
可執行任務Dispatch
Master進城內優先順序佇列
到了globalTaskDispatchWaitingQueue
中,已經是可執行任務的最小單元了
生產者
EventExecuteService
根據parent node
,對Dag進行廣度優先遍歷,提交任務到globalTaskDispatchWaitingQueue
佇列中。
消費者
消費者為GlobalTaskDispatchWaitingQueueLooper
,GlobalTaskDispatchWaitingQueueLooper
消費待dispatch的任務,根據任務型別執行任務排程,對任務的排程是走的rpc介面,目前來看根據任務型別分為兩種:
- MasterTaskDispatcher
- WorkerTaskDispatcher
對於WorkerTaskDispatcher
來說,rpc server
收到rpc request
之後提交任務到了workerTaskExecutorThreadPool
執行。所以這是一個非同步處理任務的過程,不至於讓master server
hang在這個地方。對於任務的執行進度,會在關鍵節點進行回撥通知。
任務執行狀態回撥通知
Worker被dispatch任務,非同步提交到執行緒池中之行,在任務非同步執行的節點,呼叫rpc介面通知master任務的狀態。
生產者
Worker非同步執行節點,對於任務執行狀態回撥包括四個
- TaskExecutionStatus.FAILURE 執行丟擲異常,執行失敗
- TaskExecutionStatus.RUNNING_EXECUTION 開始執行
- TaskExecutionStatus.KILL 被殺死
- TaskExecutionStatus.SUCCESS 執行成功
備註:在官方的事件流程中Ack的方向搞錯了,Ack不是worker通知給master,而是master通知workerer,我的這個事件狀態的處理結束了。
經過校正一下,比較概括性的總結,整體的流程大致如下圖
消費者
master節點ITaskInstanceExecutionEventListener
服務,服務接受rpc請求,並將任務新增到TaskEventService eventQueue
佇列中。
任務狀態處理
緩衝佇列
master節點TaskEventService eventQueue
佇列。
生產者
這個生產者可能會很多
- api-server使用者行為
- master節點任務排程
- work節點任務執行
- master任務執行
消費者
為master節點的TaskInstanceListenerImpl
服務,TaskInstanceListenerImpl
將TaskEvent
transform to TaskExecuteRunnable
,並且提交到執行緒池執行taskExecuteThreadMap
待執行,線上程池中修改任務的執行狀態。
本文由 白鯨開源 提供釋出支援!