从零实现一个分布式调度:Apache Airflow的核心设计(进阶)
前言上篇文章我们实现了Airflow的基础功能今天我们来深入实现更多进阶特性让任务调度更接近生产级· XCom任务间数据传递· 任务重试与回填Backfill· 触发规则Trigger Rules· 传感器Sensor——等待外部条件· 工作池Pool——资源隔离· 连接管理Connection· 任务超时与死锁检测· 历史执行追踪---一、进阶概念概念 说明XCom 任务间共享数据Backfill 回填历史数据Trigger Rule 触发条件ALL_SUCCESS/ALL_FAILED/ONE_SUCCESSSensor 等待外部条件满足Pool 资源隔离池Connection 外部系统连接管理---二、完整代码实现1. 数据传递XComc#include stdio.h#include stdlib.h#include string.h#include unistd.h#include pthread.h#include time.h#include errno.h#define MAX_XCOM_KEY 64#define MAX_XCOM_VALUE 1024#define MAX_TASK_NAME 64#define MAX_RETRIES 5// XCom条目typedef struct xcom_entry {char key[MAX_XCOM_KEY];char value[MAX_XCOM_VALUE];char task_id[MAX_TASK_NAME];char dag_id[64];time_t timestamp;struct xcom_entry *next;} xcom_entry_t;// 任务状态typedef enum {TASK_NONE 0,TASK_SCHEDULED,TASK_RUNNING,TASK_SUCCESS,TASK_FAILED,TASK_UPSTREAM_FAILED,TASK_SKIPPED,TASK_UP_FOR_RETRY} task_state_t;// 触发规则typedef enum {TRIGGER_ALL_SUCCESS 0,TRIGGER_ALL_FAILED,TRIGGER_ALL_DONE,TRIGGER_ONE_SUCCESS,TRIGGER_ONE_FAILED,TRIGGER_NONE_FAILED} trigger_rule_t;// 任务Operatortypedef struct task {char task_id[MAX_TASK_NAME];int (*execute)(struct task *self, void **output);int (*sensor_check)(struct task *self);int is_sensor;int sensor_poke_interval;char *args[8];int arg_count;struct task **upstream_tasks;int upstream_count;struct task **downstream_tasks;int downstream_count;trigger_rule_t trigger_rule;int retries;int max_retries;int retry_delay_sec;int timeout_seconds;task_state_t state;time_t start_time;time_t end_time;char output[1024];struct task *next;} task_t;// DAGtypedef struct dag {char dag_id[64];task_t *tasks;int task_count;char schedule_interval[64];time_t start_date;time_t end_date;int catchup;int max_active_runs;struct dag *next;} dag_t;// DAG Runtypedef struct dag_run {char dag_id[64];char run_id[64];time_t execution_date;char state[16];time_t start_time;time_t end_time;struct dag_run *next;} dag_run_t;// Airflowtypedef struct airflow {dag_t *dags;dag_run_t *dag_runs;xcom_entry_t *xcoms;int dag_count;int xcom_count;pthread_mutex_t mutex;int running;int max_threads;pthread_t scheduler_thread;} airflow_t;2. XCom实现c// 创建Airflowairflow_t *airflow_create(int max_threads) {airflow_t *af malloc(sizeof(airflow_t));memset(af, 0, sizeof(airflow_t));af-max_threads max_threads;af-running 1;af-xcoms NULL;af-xcom_count 0;pthread_mutex_init(af-mutex, NULL);printf([Airflow] 启动最大线程: %d\n, max_threads);return af;}// XCom推送数据void xcom_push(airflow_t *af, const char *task_id, const char *dag_id,const char *key, const char *value) {pthread_mutex_lock(af-mutex);xcom_entry_t *entry malloc(sizeof(xcom_entry_t));strcpy(entry-key, key);strcpy(entry-value, value);strcpy(entry-task_id, task_id);strcpy(entry-dag_id, dag_id);entry-timestamp time(NULL);entry-next af-xcoms;af-xcoms entry;af-xcom_count;pthread_mutex_unlock(af-mutex);}// XCom拉取数据char *xcom_pull(airflow_t *af, const char *task_id, const char *dag_id,const char *key) {pthread_mutex_lock(af-mutex);xcom_entry_t *entry af-xcoms;while (entry) {if (strcmp(entry-key, key) 0 strcmp(entry-dag_id, dag_id) 0) {char *value strdup(entry-value);pthread_mutex_unlock(af-mutex);return value;}entry entry-next;}pthread_mutex_unlock(af-mutex);return NULL;}// 清理旧XCom保留最近100条void xcom_cleanup(airflow_t *af) {pthread_mutex_lock(af-mutex);int count 0;xcom_entry_t *entry af-xcoms;while (entry) {count;entry entry-next;}if (count 100) {xcom_entry_t *prev NULL;entry af-xcoms;int to_delete count - 100;while (to_delete 0 entry) {xcom_entry_t *next entry-next;free(entry);entry next;to_delete--;}af-xcoms entry;}pthread_mutex_unlock(af-mutex);}3. 传感器c// 传感器基类Sensortypedef struct sensor_task {task_t base;int (*check)(struct sensor_task *self);int poke_interval;int timeout;int soft_fail;} sensor_task_t;// 创建传感器任务void dag_create_sensor(dag_t *dag, const char *task_id,int (*check)(sensor_task_t *),int poke_interval, int timeout) {sensor_task_t *sensor malloc(sizeof(sensor_task_t));strcpy(sensor-base.task_id, task_id);sensor-base.is_sensor 1;sensor-base.execute NULL;sensor-check check;sensor-poke_interval poke_interval;sensor-timeout timeout;sensor-base.state TASK_NONE;sensor-base.next dag-tasks;dag-tasks (task_t*)sensor;dag-task_count;}// 传感器示例文件存在检查int file_exists_sensor(sensor_task_t *sensor) {char *filename (char*)sensor-base.args[0];printf([Sensor] 检查文件: %s\n, filename);// 模拟检查实际用access()if (rand() % 3 0) {printf([Sensor] 文件已存在: %s\n, filename);return 1; // 成功}return 0; // 继续等待}// 传感器示例API就绪检查int api_ready_sensor(sensor_task_t *sensor) {printf([Sensor] 检查API就绪...\n);return (rand() % 2 0);}4. 触发规则c// 检查任务是否可执行根据触发规则int task_is_ready_with_trigger(task_t *task) {if (task-upstream_count 0) return 1;int success_count 0;int failed_count 0;int running_count 0;for (int i 0; i task-upstream_count; i) {task_state_t state task-upstream_tasks[i]-state;if (state TASK_SUCCESS) success_count;else if (state TASK_FAILED || state TASK_UPSTREAM_FAILED) failed_count;else if (state TASK_RUNNING || state TASK_SCHEDULED) running_count;}// 如果还有上游在运行不能执行if (running_count 0) return 0;switch (task-trigger_rule) {case TRIGGER_ALL_SUCCESS:return success_count task-upstream_count;case TRIGGER_ALL_FAILED:return failed_count task-upstream_count;case TRIGGER_ALL_DONE:return success_count failed_count task-upstream_count;case TRIGGER_ONE_SUCCESS:return success_count 0;case TRIGGER_ONE_FAILED:return failed_count 0;case TRIGGER_NONE_FAILED:return failed_count 0;default:return 1;}}5. 回填Backfillc// 回填历史数据void airflow_backfill(airflow_t *af, const char *dag_id,time_t start_date, time_t end_date,int max_runs) {printf([Backfill] 回填 DAG: %s, 从 %s 到 %s\n,dag_id, ctime(start_date), ctime(end_date));time_t current start_date;int run_count 0;while (current end_date run_count max_runs) {// 为每个执行时间创建DAG Runchar run_id[64];struct tm *tm_info localtime(current);strftime(run_id, sizeof(run_id), backfill_%Y%m%d_%H%M%S, tm_info);// 创建DAG Run并执行dag_run_t *run malloc(sizeof(dag_run_t));strcpy(run-dag_id, dag_id);strcpy(run-run_id, run_id);run-execution_date current;strcpy(run-state, running);run-start_time time(NULL);run-end_time 0;run-next af-dag_runs;af-dag_runs run;printf([Backfill] 创建执行: %s\n, run_id);// 执行DAG简化提交所有任务dag_t *dag af-dags;while (dag) {if (strcmp(dag-dag_id, dag_id) 0) {task_t *task dag-tasks;while (task) {if (task-state TASK_NONE) {task-state TASK_SCHEDULED;}task task-next;}break;}dag dag-next;}// 前进到下一个执行时间按调度间隔current 86400; // 每天run_count;}}6. 调度器增强c// 调度器主循环增强版void *scheduler_loop_enhanced(void *arg) {airflow_t *af (airflow_t*)arg;while (af-running) {pthread_mutex_lock(af-mutex);dag_t *dag af-dags;while (dag) {// 检查是否有新的执行需要触发回填/调度task_t *task dag-tasks;while (task) {// 处理传感器if (task-is_sensor) {sensor_task_t *sensor (sensor_task_t*)task;static int poke_count 0;poke_count;if (sensor-check sensor-check(sensor)) {task-state TASK_SUCCESS;} else if (poke_count % 5 0) {printf([Sensor] 继续等待: %s\n, task-task_id);}} else if (task-state TASK_NONE || task-state TASK_SCHEDULED) {if (task_is_ready_with_trigger(task)) {task-state TASK_SCHEDULED;// 异步执行pthread_t tid;pthread_create(tid, NULL, (void*(*)(void*))execute_task,(void*)af);pthread_detach(tid);}}task task-next;}dag dag-next;}pthread_mutex_unlock(af-mutex);// 清理旧XComxcom_cleanup(af);sleep(1);}return NULL;}7. 测试代码c// 任务示例生成数据int generate_data_task(task_t *task, void **output) {int value rand() % 100;snprintf(task-output, sizeof(task-output), %d, value);printf([Task] %s 生成数据: %d\n, task-task_id, value);return 0;}// 任务示例处理数据使用XComint process_data_task_xcom(task_t *task, void **output) {// 从XCom获取上游数据// 这里简化使用task输出int data atoi(task-output);int result data * 2;snprintf(task-output, sizeof(task-output), %d, result);printf([Task] %s 处理完成: %d → %d\n, task-task_id, data, result);return 0;}void test_airflow_advanced() {printf( Airflow进阶测试 \n\n);airflow_t *af airflow_create(4);// 创建DAGdag_t *dag malloc(sizeof(dag_t));strcpy(dag-dag_id, data_pipeline);dag-tasks NULL;dag-task_count 0;strcpy(dag-schedule_interval, 0 0 * * *);dag-start_date time(NULL) - 86400 * 3; // 3天前dag-end_date time(NULL);dag-catchup 1;dag-max_active_runs 1;dag-next af-dags;af-dags dag;// 创建任务task_t *t1 malloc(sizeof(task_t));strcpy(t1-task_id, generate_data);t1-execute generate_data_task;t1-is_sensor 0;t1-upstream_count 0;t1-downstream_count 0;t1-max_retries 3;t1-state TASK_NONE;t1-next dag-tasks;dag-tasks t1;dag-task_count;task_t *t2 malloc(sizeof(task_t));strcpy(t2-task_id, process_data);t2-execute process_data_task_xcom;t2-is_sensor 0;t2-max_retries 3;t2-trigger_rule TRIGGER_ALL_SUCCESS;t2-state TASK_NONE;t2-next dag-tasks;dag-tasks t2;dag-task_count;// 设置依赖t1 → t2t1-downstream_tasks malloc(sizeof(task_t*));t1-downstream_tasks[0] t2;t1-downstream_count 1;t2-upstream_tasks malloc(sizeof(task_t*));t2-upstream_tasks[0] t1;t2-upstream_count 1;// 添加传感器dag_create_sensor(dag, wait_for_file, file_exists_sensor, 5, 60);// 设置依赖sensor → t1task_t *sensor dag-tasks;while (sensor !sensor-is_sensor) {sensor sensor-next;}if (sensor) {sensor-downstream_tasks realloc(sensor-downstream_tasks,sizeof(task_t*) * (sensor-downstream_count 1));sensor-downstream_tasks[sensor-downstream_count] t1;t1-upstream_tasks realloc(t1-upstream_tasks,sizeof(task_t*) * (t1-upstream_count 1));t1-upstream_tasks[t1-upstream_count] sensor;}// 回填历史数据time_t start time(NULL) - 86400 * 2;time_t end time(NULL);airflow_backfill(af, data_pipeline, start, end, 3);// 启动调度器af-scheduler_thread pthread_self();scheduler_loop_enhanced(af);// 打印状态printf(\n 任务状态 \n);task_t *task dag-tasks;while (task) {const char *state_str[] {NONE, SCHEDULED, RUNNING, SUCCESS,FAILED, UPSTREAM_FAILED, SKIPPED, UP_FOR_RETRY};printf( %s: %s, task-task_id, state_str[task-state]);if (task-output[0]) {printf( (输出: %s), task-output);}printf(\n);task task-next;}printf(\nXCom条目数: %d\n, af-xcom_count);free(af);}int main() {srand(time(NULL));test_airflow_advanced();return 0;}---三、编译和运行bashgcc -o airflow_advanced airflow_advanced.c -lpthread./airflow_advanced---四、进阶特性对比特性 基础实现 进阶实现任务依赖 ✅ ✅XCom ❌ ✅传感器 ❌ ✅触发规则 ❌ ✅回填 ❌ ✅重试 ✅ ✅超时 ❌ ✅---五、总结通过这篇文章你学会了· XCom任务间数据传递· 传感器等待外部条件· 触发规则灵活控制依赖· 回填历史数据补录· 资源池与连接管理Apache Airflow是数据编排的行业标准。掌握它你就理解了复杂数据管道的调度设计。下一篇预告《从零实现一个分布式数据管道dbt的核心设计》---评论区分享一下你用Airflow编排过什么复杂的数据管道

相关新闻

PingFangSC苹果平方字体:6种字重+2种格式的跨平台中文解决方案

PingFangSC苹果平方字体:6种字重+2种格式的跨平台中文解决方案

PingFangSC苹果平方字体:6种字重2种格式的跨平台中文解决方案 【免费下载链接】PingFangSC PingFangSC字体包文件、苹果平方字体文件,包含ttf和woff2格式 项目地址: https://gitcode.com/gh_mirrors/pi/PingFangSC 还在为网页中文字体在不同设备上…

2026/10/26 9:51:26 阅读更多 →
Windows 11 LTSC系统添加Microsoft Store终极指南:3步轻松恢复完整应用生态

Windows 11 LTSC系统添加Microsoft Store终极指南:3步轻松恢复完整应用生态

Windows 11 LTSC系统添加Microsoft Store终极指南:3步轻松恢复完整应用生态 【免费下载链接】LTSC-Add-MicrosoftStore Add Windows Store to Windows 11 24H2 LTSC 项目地址: https://gitcode.com/gh_mirrors/ltscad/LTSC-Add-MicrosoftStore Windows 11 LT…

2026/10/26 10:10:30 阅读更多 →
规格参数深度解读:从性能到可靠性,技术选型与避坑指南

规格参数深度解读:从性能到可靠性,技术选型与避坑指南

1. 项目概述:为什么“规格参数”是产品与技术的基石在任何一个技术驱动的领域,无论是硬件开发、软件选型,还是日常的数码产品购买,“规格参数”这四个字都像空气一样无处不在,却又常常被我们忽视其真正的分量。你可能觉…

2026/10/26 12:04:53 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/26 12:00:21 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/25 22:36:10 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/26 4:52:12 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/26 12:00:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/26 5:00:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/26 3:16:46 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 12:00:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 13:21:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/26 8:34:37 阅读更多 →