轉載自程式設計師小陶
Apache DolphinScheduler 在使用過程中,肯定會有任務出現失敗的情況,那麼問題來了:排程任務的告警是需要人為配置的,在生產環境中,面對海量的任務,如何找到重要的任務,並且在失敗的時候,第一時間告警呢?
先思考一下。
先看思路
本文提供一個思路,接著往下看吧。
不賣關子了。
本質是路徑查詢,本文這裡使用了圖資料庫,或者你也可以自己使用Java實現路徑查詢。
下面是需要實現的目標,看一組任務的關係,如下圖所示,存在 A/B/C/D/E 五個任務,E 任務被配置為核心任務,當 B 任務報錯時,檢測到 B 和 E 之前存在路徑,則需要電話告警。
所以在配置核心鏈路告警的時候,我們只需要配置葉子節點,在實際生產中,一般是應用層的任務,比如報表、標籤、介面資料等任務。
清洗依賴資料
核心邏輯就是把所有工作流內部、跨工作流以及跨專案的依賴全部清洗出來,生成一張關係表。具體清洗邏輯,可以看:海豚排程監控:新增依賴缺失巡檢,上游改動再也不用擔心了!
最終生成了
t_ds_task_node_base_data
任務基礎表,後續會用於 Nebula Graph,這個後面會講。
t_ds_dag_task_relation_data_df
關係最終表,後續會用於 Nebula Graph,這個後面會講。
t_ds_dag_task_relation_data_df
這個表結構如下:
關係匯入圖資料庫
這裡用的國產圖資料庫 Nebula Graph,當然你也可以自己使用 Java 實現路徑查詢。
為什麼我們一定要引入圖資料庫呢?有下面幾方面考慮:
- 可以減輕排程系統Mysql的壓力,把負責的路徑計算放在圖資料庫裡面。
- 探索更多排程任務資料治理和運維的可能性,比如任務權重,影響分析等。
用到的元件是 Nebula Graph,最關鍵的函式是 find path 查詢最短鏈路
① 用到的語法是:FIND SHORTEST PATH
需要注意的是,注意查詢步長,UPTO <N> {STEP|STEPS}:
路徑的最大跳數。預設值為5。
② 3.3.0 開始,子圖支援了邊的條件限制了,查詢的時候只拿最新的一批關係。
- 建立圖空間
CREATE SPACE s_schedule_job (partition_num = 225, replica_factor = 3, vid_type = FIXED_STRING(180)) COMMENT = "大資料平臺排程系統任務的血緣關係";
- 建立邊和點
## 任務標籤
DROP tag if exists t_task;
CREATE tag if not exists t_task( id string NULL COMMENT "project_code,dag_code,task_code,拼接,", project_name string NULL COMMENT "project_name", project_code string NULL COMMENT "project_code", dag_name string NULL COMMENT "dag_name", dag_code string NULL COMMENT "dag_code", dag_version string NULL COMMENT "dag_version", task_code string NULL COMMENT "task_code", task_version string NULL COMMENT "task_version", task_name string NULL COMMENT "task_name", task_type string NULL COMMENT "task_type", create_time string NULL COMMENT "時間戳") comment='排程任務節點';
## 排程任務關係
drop edge if exists e_task;
create edge if not exists e_task( pre_project_name string NULL COMMENT "project_name", pre_project_code string NULL COMMENT "project_code", pre_dag_name string NULL COMMENT "dag_name", pre_dag_code string NULL COMMENT "dag_code", pre_dag_version string NULL COMMENT "dag_version", pre_task_code string NULL COMMENT "task_code", pre_task_version string NULL COMMENT "task_version", pre_task_name string NULL COMMENT "task_name", pre_task_type string NULL COMMENT "task_type", post_project_name string NULL COMMENT "project_name", post_project_code string NULL COMMENT "project_code", post_dag_name string NULL COMMENT "dag_name", post_dag_code string NULL COMMENT "dag_code", post_dag_version string NULL COMMENT "dag_version", post_task_code string NULL COMMENT "task_code", post_task_version string NULL COMMENT "task_version", post_task_name string NULL COMMENT "task_name", post_task_type string NULL COMMENT "task_type", create_time string NULL COMMENT "時間戳") comment='排程任務關係';
- 匯入資料
同步點:
{
spark: {
app: {
name: Nebula_Exchange_t_task
}
driver: {
cores: 2
maxResultSize: 5G
}
}
nebula: {
address:{
graph:["10.1.x.xx:9669","10.1.x.xx:9669","10.1.x.xx:9669","10.1.x.xx3:9669","10.1.x.xx:9669"]
meta:["10.1.x.xx:9559","10.1.x.xx:9559","10.1.x.xx:9559"]
}
user: root
pswd: "nebula密碼"
space: s_schedule_job
connection {
timeout: 60000
retry: 3
}
execution {
retry: 3
}
error: {
max: 32
output: /tmp/errors/t_task
}
rate: {
limit: 1024
timeout: 10000
}
}
tags: [
{
name: t_task
type: {
source: mysql
sink: client
}
host:"排程系統MYSQL資料庫IP"
port:3307
database:"排程系統MYSQL資料庫"
table:"t_ds_task_node_base_data"
user:"排程系統MYSQL使用者"
password:"排程系統MYSQL使用者密碼"
sentence:"SELECT concat(project_code,'_',dag_code,'_',task_code) as id,project_name,project_code,dag_name,dag_code,dag_version,task_code,task_version,task_name,task_type,create_time FROM t_ds_task_node_base_data"
fields: [project_name,project_code,dag_name,dag_code,dag_version,task_code,task_version,task_name,task_type,create_time]
nebula.fields: [project_name,project_code,dag_name,dag_code,dag_version,task_code,task_version,task_name,task_type,create_time]
vertex:{
field:id
}
batch: 256
partition: 32
}
]
}
同步邊:
{
spark: {
app: {
name: Nebula_Exchange_e_task
}
driver: {
cores: 2
maxResultSize: 5G
}
}
nebula: {
address:{
graph:["10.1.x.xx:9669","10.1.x.xx:9669","10.1.x.xx:9669","10.1.x.xx3:9669","10.1.x.xx:9669"]
meta:["10.1.x.xx:9559","10.1.x.xx:9559","10.1.x.xx:9559"]
}
user: root
pswd: "aD@VX2018#"
space: s_schedule_job
connection {
timeout: 60000
retry: 3
}
execution {
retry: 3
}
error: {
max: 32
output: /tmp/errors/e_task
}
rate: {
limit: 1024
timeout: 10000
}
}
edges: [
{
name: e_task
type: {
source: mysql
sink: client
}
host:"排程系統MYSQL資料庫IP"
port:3307
database:"排程系統MYSQL資料庫"
table:"t_ds_task_node_base_data"
user:"排程系統MYSQL使用者"
password:"排程系統MYSQL使用者密碼"
sentence:"SELECT concat(pre_project_code,'_',pre_dag_code,'_',pre_task_code) as from_id,concat(post_project_code,'_',post_dag_code,'_',post_task_code) as to_id,pre_project_name,pre_project_code,pre_dag_name,pre_dag_code,pre_dag_version,pre_task_code,pre_task_name,pre_task_type,pre_task_version,post_project_name,post_project_code,post_dag_name,post_dag_code,post_dag_version,post_task_code,post_task_name,post_task_type,post_task_version,create_time FROM t_ds_dag_task_relation_data_df"
fields: [pre_project_name,pre_project_code,pre_dag_name,pre_dag_code,pre_dag_version,pre_task_code,pre_task_name,pre_task_type,pre_task_version,post_project_name,post_project_code,post_dag_name,post_dag_code,post_dag_version,post_task_code,post_task_name,post_task_type,post_task_version,create_time]
nebula.fields: [pre_project_name,pre_project_code,pre_dag_name,pre_dag_code,pre_dag_version,pre_task_code,pre_task_name,pre_task_type,pre_task_version,post_project_name,post_project_code,post_dag_name,post_dag_code,post_dag_version,post_task_code,post_task_name,post_task_type,post_task_version,create_time]
source: {
field: from_id
}
target: {
field: to_id
}
batch: 256
partition: 225
}
]
}
定時指令碼: 使用 Nebula Graph 社群提供的 exchange 工具把資料從 mysql 匯入 Nebula Graph。
#!/bin/bash
# 作業引數
basepath='/opt/vcredit-graph-db/s_schedule_job/exchange'
tmpdir='/tmp/nebula/s_schedule_job'
mkdir -p $tmpdir
sourcefile=${basepath}/${jobname}.conf
targetfile=${tmpdir}/${jobname}_${vardate}.conf
cat ${sourcefile} > ${targetfile}
sed -i "s/vardate/${vardate}/g" ${targetfile}
sed -i "s/varhivetable/${varhivetable}/g" ${targetfile}
# 執行環境
export JAVA_HOME=/usr/java/jdk1.8.0_181-cloudera
spark_submit="/opt/spark-2.4.8-bin-hadoop2.7/bin/spark-submit"
# 開始執行
${spark_submit} \
--principal hive@VCREDIT.COM \
--keytab /etc/security/hive.keytab \
--master "local[*]" \
--class com.vesoft.nebula.exchange.Exchange /opt/nebula/nebula-exchange_spark_2.4-3.0.0.jar -c ${targetfile} -h
Java 服務
/**
* 判斷這個任務是否會影響核心任務
* @param projectName
* @param dagName
* @param taskName
* @return
*/
@ApiOperation(value = "dolphinTaskIsOnCall", notes = "判斷這個任務是否會影響核心任務,是 1 ,否 0")
@ApiImplicitParams({
@ApiImplicitParam(name = "projectName", value = "T-1", required = false, dataType = "String", example = "BigData"),
@ApiImplicitParam(name = "dagName", value = "T-1", required = false, dataType = "String", example = "公共和自定義域(pub)_daily"),
@ApiImplicitParam(name = "taskName", value = "T-1", required = false, dataType = "String", example = "dwd_pub_screen_zxd_cust_df")
})
@GetMapping("/dolphinTaskIsOnCall")
@ResponseBody
public DataResult dolphinTaskIsOnCall(
@RequestParam(value = "projectName", required = true) String projectName,
@RequestParam(value = "dagName", required = true) String dagName,
@RequestParam(value = "taskName", required = true) String taskName) throws GraphDatabaseException, UnsupportedEncodingException {
HashMap<String,Object> res = dolphinService.dolphinTaskIsOnCall(projectName, dagName, taskName);
return DataResult.ok(res);
}
核心程式碼,在第 17 行:
@Override
public HashMap<String, Object> dolphinTaskIsOnCall(String projectName, String dagName, String taskName) throws GraphDatabaseException, UnsupportedEncodingException {
HashMap<String,Object> resMap = new HashMap<>();
// 查詢該任務 codes
HashMap<String,Object> task = dolphinTaskInstanceMapper.getTaskCode(projectName,dagName,taskName);
if (task == null){
resMap.put("res","任務不存在!");
return resMap;
}
String fromCodes = task.get("project_code") + "_" + task.get("dag_code") + "_" + task.get("task_code");
// 查詢核心任務 codes
List<HashMap<String,Object>> tasks = dolphinTaskInstanceMapper.getOnCallTasks();
// 查詢最短鏈路
for (HashMap<String,Object> t : tasks){
String toCodes = t.get("project_code") + "_" + t.get("dag_code") + "_" + t.get("task_code");
// 查詢Nebula
String NgSql = "FIND SHORTEST PATH with PROP FROM \"" + fromCodes + "\" TO \"" + toCodes + "\" OVER * WHERE e_task.create_time > '" + DateUtils.dayToString(DateUtils.getSomeDay(new Date(), -1)) + "' UPTO 100 STEPS YIELD path AS p;";
int res = nebulaService.isOnCallTask("s_schedule_job",NgSql);
if (res > 0){
resMap.put("res",res);
return resMap;
}
}
resMap.put("res",0);
return resMap;
}
返回值說明:
① 影響核心任務,需要打電話
② 不影響核心任務,不需要打電話
③ 任務不存在,忽略
④ code 不等於 0 ,介面異常,忽略。
封裝好介面之後,任務失敗的程式調這個介面,判斷失敗任務是否影響核心任務,如果影響就打電話。
釘釘告警樣式:
電話告警,直接給對應負責人打電話。
至此,我們減少了很多工告警的配置工作,只需要關注核心的葉子節點是什麼,也就是核心的應用任務是什麼,大大提高了任務告警的配置效率!!!
- 注意:清洗資料 和 匯入圖資料庫,在每天的 23:30 分進行,一天初始化一次,確保凌晨的任務關係是最新的,主要是用於凌晨告警。
以上就使用圖關係網路解決核心鏈路告警的全部內容,如果有任何疑問,都可以與我交流,希望可以幫到你,下次見。
原文連結:https://blog.csdn.net/qq_31975963/article/details/139839102
本文由 白鯨開源 提供釋出支援!