从零实现一个分布式任务调度器:XXL-JOB的核心设计

前言

你有没有想过:每天凌晨3点,系统是怎么自动执行数据同步、报表生成、缓存刷新这些定时任务的?如果任务执行失败了,系统会怎么处理?

分布式任务调度器是微服务架构中处理定时任务、异步任务的核心组件。

今天我们用C语言从零实现一个分布式任务调度器的核心功能:

· 任务注册与发现

· Cron表达式解析

· 任务分片(并行执行)

· 故障转移(容错处理)

· 任务状态管理

· 执行日志

· 监控告警


一、调度器核心原理

  1. 架构图

```

┌─────────────────────────────────────────────────────────────┐

│ 调度器集群 │

│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │

│ │ 节点1 │ │ 节点2 │ │ 节点3 │ │

│ │ (Leader) │ │ (Follower) │ │ (Follower) │ │

│ └──────┬──────┘ └─────────────┘ └─────────────┘ │

│ │ │

│ ▼ │

│ ┌─────────────────────────────────────────────────────┐ │

│ │ 任务分片策略 │ │

│ │ 0-33% 33%-66% 66%-100% │ │

│ └─────────────────────────────────────────────────────┘ │

└─────────────────────────────────────────────────────────────┘

│ │ │

▼ ▼ ▼

┌─────────┐ ┌─────────┐ ┌─────────┐

│ 执行器1 │ │ 执行器2 │ │ 执行器3 │

│ 执行任务 │ │ 执行任务 │ │ 执行任务 │

└─────────┘ └─────────┘ └─────────┘

```

  1. 核心概念

概念 说明

任务 需要定时执行的业务逻辑

执行器 执行任务的工作节点

分片 大任务拆分成小片并行执行

故障转移 节点宕机,任务转移给其他节点

Cron 定时表达式


二、完整代码实现

  1. 基础数据结构

```c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <pthread.h>

#include <time.h>

#include <errno.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#define MAX_TASK_NAME 128

#define MAX_EXECUTOR 64

#define MAX_SHARDS 100

#define MAX_LOG_LEN 1024

// 任务状态

typedef enum {

TASK_IDLE = 0,

TASK_RUNNING,

TASK_SUCCESS,

TASK_FAILED,

TASK_TIMEOUT,

TASK_CANCELLED

} task_status_t;

// 任务分片

typedef struct task_shard {

int shard_index;

int total_shards;

char executorMAX_EXECUTOR;

task_status_t status;

time_t start_time;

time_t end_time;

char error_msg256;

struct task_shard *next;

} task_shard_t;

// 任务定义

typedef struct task_definition {

char nameMAX_TASK_NAME;

char cron_expr64;

int shard_count;

int timeout_seconds;

int retry_count;

void (*execute)(task_shard_t *shard);

struct task_definition *next;

} task_definition_t;

// 任务实例

typedef struct task_instance {

char task_nameMAX_TASK_NAME;

char instance_id64;

task_shard_t *shards;

int shard_count;

int completed_shards;

task_status_t status;

time_t start_time;

time_t end_time;

struct task_instance *next;

} task_instance_t;

// 执行器节点

typedef struct executor_node {

char nameMAX_EXECUTOR;

char host32;

int port;

int healthy;

time_t last_heartbeat;

struct executor_node *next;

} executor_node_t;

// 任务日志

typedef struct task_log {

char task_nameMAX_TASK_NAME;

char instance_id64;

int shard_index;

time_t log_time;

char level16;

char messageMAX_LOG_LEN;

struct task_log *next;

} task_log_t;

// 任务调度器

typedef struct task_scheduler {

task_definition_t *tasks;

task_instance_t *instances;

executor_node_t *executors;

task_log_t *logs;

int executor_count;

int running;

pthread_mutex_t mutex;

pthread_t schedule_thread;

pthread_t health_check_thread;

int port;

} task_scheduler_t;

```

  1. 任务注册

```c

// 创建调度器

task_scheduler_t *scheduler_create(int port) {

task_scheduler_t *s = malloc(sizeof(task_scheduler_t));

memset(s, 0, sizeof(task_scheduler_t));

s->port = port;

s->running = 1;

pthread_mutex_init(&s->mutex, NULL);

printf("调度器 启动,端口: %d\n", port);

return s;

}

// 注册任务

void scheduler_register_task(task_scheduler_t *s, const char *name,

const char *cron_expr, int shard_count,

int timeout_seconds, int retry_count,

void (*execute)(task_shard_t*)) {

pthread_mutex_lock(&s->mutex);

task_definition_t *task = malloc(sizeof(task_definition_t));

strcpy(task->name, name);

strcpy(task->cron_expr, cron_expr);

task->shard_count = shard_count > 0 ? shard_count : 1;

task->timeout_seconds = timeout_seconds > 0 ? timeout_seconds : 300;

task->retry_count = retry_count > 0 ? retry_count : 3;

task->execute = execute;

task->next = s->tasks;

s->tasks = task;

pthread_mutex_unlock(&s->mutex);

printf("任务 注册: %s, cron=%s, shards=%d\n", name, cron_expr, shard_count);

}

// 注册执行器

void scheduler_register_executor(task_scheduler_t *s, const char *name,

const char *host, int port) {

pthread_mutex_lock(&s->mutex);

executor_node_t *node = malloc(sizeof(executor_node_t));

strcpy(node->name, name);

strcpy(node->host, host);

node->port = port;

node->healthy = 1;

node->last_heartbeat = time(NULL);

node->next = s->executors;

s->executors = node;

s->executor_count++;

pthread_mutex_unlock(&s->mutex);

printf("执行器 注册: %s (%s:%d)\n", name, host, port);

}

```

  1. Cron解析

```c

// Cron字段

typedef struct cron_fields {

int minute60;

int minute_count;

int hour24;

int hour_count;

int day31;

int day_count;

int month12;

int month_count;

int weekday7;

int weekday_count;

} cron_fields_t;

// 解析Cron表达式

int cron_parse(const char *expr, cron_fields_t *fields) {

char parts632;

int count = sscanf(expr, "%s %s %s %s %s",

parts0, parts1, parts2, parts3, parts4);

if (count != 5) return -1;

// 解析分钟

if (strcmp(parts0, "*") == 0) {

fields->minute_count = 60;

for (int i = 0; i < 60; i++) fields->minutei = i;

} else {

fields->minute_count = 1;

fields->minute0 = atoi(parts0);

}

// 解析小时

if (strcmp(parts1, "*") == 0) {

fields->hour_count = 24;

for (int i = 0; i < 24; i++) fields->houri = i;

} else {

fields->hour_count = 1;

fields->hour0 = atoi(parts1);

}

// 解析日

if (strcmp(parts2, "*") == 0) {

fields->day_count = 31;

for (int i = 1; i <= 31; i++) fields->dayi-1 = i;

} else {

fields->day_count = 1;

fields->day0 = atoi(parts2);

}

// 解析月

if (strcmp(parts3, "*") == 0) {

fields->month_count = 12;

for (int i = 1; i <= 12; i++) fields->monthi-1 = i;

} else {

fields->month_count = 1;

fields->month0 = atoi(parts3);

}

// 解析周

if (strcmp(parts4, "*") == 0) {

fields->weekday_count = 7;

for (int i = 0; i < 7; i++) fields->weekdayi = i;

} else {

fields->weekday_count = 1;

fields->weekday0 = atoi(parts4);

}

return 0;

}

int cron_match(cron_fields_t *fields, struct tm *tm) {

int match = 0;

for (int i = 0; i < fields->minute_count; i++) {

if (fields->minutei == tm->tm_min) { match = 1; break; }

}

if (!match) return 0;

match = 0;

for (int i = 0; i < fields->hour_count; i++) {

if (fields->houri == tm->tm_hour) { match = 1; break; }

}

if (!match) return 0;

match = 0;

for (int i = 0; i < fields->day_count; i++) {

if (fields->dayi == tm->tm_mday) { match = 1; break; }

}

if (!match) return 0;

match = 0;

for (int i = 0; i < fields->month_count; i++) {

if (fields->monthi == tm->tm_mon + 1) { match = 1; break; }

}

if (!match) return 0;

match = 0;

for (int i = 0; i < fields->weekday_count; i++) {

if (fields->weekdayi == tm->tm_wday) { match = 1; break; }

}

return match;

}

```

  1. 任务调度

```c

// 执行分片任务

void execute_shard(task_scheduler_t *s, task_definition_t *task,

task_instance_t *instance, int shard_idx) {

task_shard_t *shard = &instance->shardsshard_idx;

shard->shard_index = shard_idx;

shard->total_shards = task->shard_count;

strcpy(shard->executor, "local");

shard->status = TASK_RUNNING;

shard->start_time = time(NULL);

printf("执行 分片 %d/%d: %s\n", shard_idx+1, task->shard_count, task->name);

if (task->execute) {

task->execute(shard);

shard->status = TASK_SUCCESS;

}

shard->end_time = time(NULL);

pthread_mutex_lock(&s->mutex);

instance->completed_shards++;

if (instance->completed_shards >= task->shard_count) {

instance->status = TASK_SUCCESS;

instance->end_time = time(NULL);

}

pthread_mutex_unlock(&s->mutex);

}

// 检查并触发任务

void scheduler_check_tasks(task_scheduler_t *s) {

time_t now = time(NULL);

struct tm *tm_now = localtime(&now);

pthread_mutex_lock(&s->mutex);

task_definition_t *task = s->tasks;

while (task) {

cron_fields_t fields;

if (cron_parse(task->cron_expr, &fields) == 0) {

if (cron_match(&fields, tm_now)) {

// 检查是否有实例在运行

task_instance_t *inst = s->instances;

int running = 0;

while (inst) {

if (strcmp(inst->task_name, task->name) == 0 &&

inst->status == TASK_RUNNING) {

running = 1;

break;

}

inst = inst->next;

}

if (!running) {

// 创建新任务实例

task_instance_t *new_inst = malloc(sizeof(task_instance_t));

strcpy(new_inst->task_name, task->name);

snprintf(new_inst->instance_id, sizeof(new_inst->instance_id),

"%s-%ld", task->name, now);

new_inst->shard_count = task->shard_count;

new_inst->shards = calloc(task->shard_count, sizeof(task_shard_t));

new_inst->completed_shards = 0;

new_inst->status = TASK_RUNNING;

new_inst->start_time = now;

new_inst->end_time = 0;

new_inst->next = s->instances;

s->instances = new_inst;

printf("调度 触发任务: %s\n", task->name);

// 执行所有分片

for (int i = 0; i < task->shard_count; i++) {

execute_shard(s, task, new_inst, i);

}

}

}

}

task = task->next;

}

pthread_mutex_unlock(&s->mutex);

}

```

  1. 故障转移

```c

// 检查执行器健康

void scheduler_check_health(task_scheduler_t *s) {

pthread_mutex_lock(&s->mutex);

time_t now = time(NULL);

executor_node_t *node = s->executors;

while (node) {

if (now - node->last_heartbeat > 30) {

if (node->healthy) {

node->healthy = 0;

printf("告警 执行器 %s 失联\n", node->name);

}

}

node = node->next;

}

pthread_mutex_unlock(&s->mutex);

}

// 故障转移

void scheduler_failover(task_scheduler_t *s) {

pthread_mutex_lock(&s->mutex);

time_t now = time(NULL);

task_instance_t *inst = s->instances;

while (inst) {

if (inst->status == TASK_RUNNING) {

for (int i = 0; i < inst->shard_count; i++) {

if (inst->shardsi.status == TASK_RUNNING) {

time_t elapsed = now - inst->shardsi.start_time;

if (elapsed > 300) {

inst->shardsi.status = TASK_FAILED;

strcpy(inst->shardsi.error_msg, "Timeout, failover");

printf("故障转移 分片 %d 超时\n", i);

}

}

}

}

inst = inst->next;

}

pthread_mutex_unlock(&s->mutex);

}

```

  1. 测试代码

```c

// 示例任务

void data_sync_task(task_shard_t *shard) {

printf("任务 数据同步 分片 %d/%d\n", shard->shard_index+1, shard->total_shards);

sleep(1);

}

void report_task(task_shard_t *shard) {

printf("任务 报表生成 分片 %d/%d\n", shard->shard_index+1, shard->total_shards);

sleep(2);

}

void test_scheduler() {

printf("=== 分布式任务调度器测试 ===\n\n");

task_scheduler_t *s = scheduler_create(8080);

// 注册任务

scheduler_register_task(s, "data-sync", "0 0 3 * * *", 4, 300, 3, data_sync_task);

scheduler_register_task(s, "report-gen", "0 30 2 * * *", 2, 600, 2, report_task);

// 注册执行器

scheduler_register_executor(s, "executor-1", "127.0.0.1", 9001);

scheduler_register_executor(s, "executor-2", "127.0.0.1", 9002);

// 模拟调度(手动触发)

printf("\n模拟调度任务...\n");

scheduler_check_tasks(s);

printf("\n任务实例:\n");

task_instance_t *inst = s->instances;

while (inst) {

printf(" %s: 状态=%d, 完成=%d/%d\n",

inst->task_name, inst->status,

inst->completed_shards, inst->shard_count);

inst = inst->next;

}

free(s);

}

int main() {

test_scheduler();

return 0;

}

```


三、编译和运行

```bash

gcc -o task_scheduler task_scheduler.c -lpthread

./task_scheduler

```


四、XXL-JOB vs 本实现

特性 本实现 XXL-JOB

任务注册 ✅ ✅

Cron调度 ✅ ✅

任务分片 ✅ ✅

故障转移 ✅ ✅

执行器管理 ✅ ✅

可视化界面 ❌ ✅

任务依赖 ❌ ❌

任务编排 ❌ ❌


五、总结

通过这篇文章,你学会了:

· 分布式调度器的核心原理

· Cron表达式解析

· 任务分片与并行执行

· 故障转移机制

· 执行器健康检查

· 任务状态管理

分布式调度器是大数据平台和微服务架构的核心组件。掌握它,你就理解了数据同步、报表生成、定时清理等系统的设计原理。

下一篇预告:《从零实现一个分布式ID生成器:雪花算法与号段模式》


评论区分享一下你用调度器解决过什么场景~

相关推荐
一拳一个呆瓜2 小时前
【STL】iostream 编程:缓冲区的作用
c++·stl
凯瑟琳.奥古斯特3 小时前
力扣1012数位DP解法详解
开发语言·c++·算法·leetcode·职场和发展
chh5633 小时前
C++--string
java·开发语言·网络·c++·学习
J_yyy4 小时前
基于Reactor的文件管理服务开发笔记
c++·笔记·reactor·多线程编程·muduo
李小小钦4 小时前
D. Storming Arasaka(Codeforces 2238)
c语言·开发语言·数据结构·c++·算法
卷无止境5 小时前
SFML 深度解读:一个教科书级 C++ 多媒体库的内功心法
c++·后端
jinyishu_5 小时前
C++ 类和对象
开发语言·c++
小徐不徐说6 小时前
Qt 线程迁移机制完整实战指南(moveToThread)
开发语言·c++·qt·程序设计
盐焗鹌鹑蛋6 小时前
【C++】set和map
c++