从零实现一个分布式流处理:Apache Flink的核心设计

前言

在实时计算领域,数据是持续不断产生的,不能等待批量处理。Apache Flink 是流处理领域的标杆,实现了真正的流计算(而非微批处理)。

今天我们从零实现Flink的核心功能:

· 数据源(Source)与数据汇(Sink)

· 转换算子(Map/Filter/FlatMap)

· 窗口(Window)

· 时间语义(Event Time/Processing Time)

· 状态管理(State)

· 检查点(Checkpoint)

· 容错与恢复


一、Flink核心原理

  1. 架构图

```

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

│ 数据流 │

│ Source → Map → Filter → Window → Reduce → Sink │

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

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

│ Flink运行时 │

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

│ │ JobManager│ │ TaskManager│ │ TaskManager│ │

│ │ (调度) │ │ (执行) │ │ (执行) │ │

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

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

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

│ 状态后端 │

│ (内存 / RocksDB / HDFS) │

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

```

  1. 核心概念

概念 说明

Source 数据源(Kafka/Socket/File)

Sink 数据汇(输出)

Transformation 转换操作(Map/Filter/KeyBy)

Window 窗口(滚动/滑动/会话)

State 状态(算子状态/键控状态)

Checkpoint 快照(容错)

Time 时间语义(Event/Processing/Ingestion)


二、完整代码实现

  1. 基础数据结构

```c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <pthread.h>

#include <time.h>

#include <errno.h>

#include <math.h>

#define MAX_EVENT_SIZE 1024

#define MAX_STREAM_NAME 64

#define MAX_OPERATORS 20

#define MAX_WATERMARK_MS 5000

// 事件/数据

typedef struct event {

char dataMAX_EVENT_SIZE;

long long timestamp; // 事件时间

long long processing_time; // 处理时间

char key64;

struct event *next;

} event_t;

// 窗口类型

typedef enum {

WINDOW_TUMBLING = 0, // 滚动窗口

WINDOW_SLIDING = 1, // 滑动窗口

WINDOW_SESSION = 2 // 会话窗口

} window_type_t;

// 窗口定义

typedef struct window_def {

window_type_t type;

long long size_ms;

long long slide_ms;

long long gap_ms;

struct window_def *next;

} window_def_t;

// 状态

typedef struct state {

char key64;

void *value;

size_t value_size;

long long last_update;

struct state *next;

} state_t;

// 算子函数类型

typedef event_t* (*map_func_t)(event_t *e);

typedef event_t* (*filter_func_t)(event_t *e);

typedef event_t* (*flatmap_func_t)(event_t *e, event_t **output);

// 算子

typedef struct operator {

char name64;

int op_type; // 0: map, 1: filter, 2: flatmap, 3: keyby, 4: window, 5: reduce

map_func_t map_func;

filter_func_t filter_func;

flatmap_func_t flatmap_func;

char key_field64;

window_def_t *window_def;

struct operator *next;

} operator_t;

// 数据流

typedef struct data_stream {

char nameMAX_STREAM_NAME;

operator_t *operators;

int operator_count;

struct data_stream *next;

} data_stream_t;

// 执行环境

typedef struct flink_env {

data_stream_t *streams;

int stream_count;

pthread_mutex_t mutex;

int running;

int parallelism;

int checkpoint_interval_ms;

long long current_watermark;

} flink_env_t;

// 数据源

typedef struct source {

char name64;

event_t* (*generate)(void *ctx);

void *ctx;

pthread_t thread;

int running;

struct source *next;

} source_t;

```

  1. 执行环境

```c

// 创建Flink环境

flink_env_t *flink_create(int parallelism, int checkpoint_interval_ms) {

flink_env_t *env = malloc(sizeof(flink_env_t));

memset(env, 0, sizeof(flink_env_t));

env->parallelism = parallelism;

env->checkpoint_interval_ms = checkpoint_interval_ms;

env->running = 1;

env->current_watermark = 0;

pthread_mutex_init(&env->mutex, NULL);

printf("Flink 环境创建,并行度: %d\n", parallelism);

return env;

}

// 创建数据流

data_stream_t *flink_add_stream(flink_env_t *env, const char *name) {

pthread_mutex_lock(&env->mutex);

data_stream_t *stream = malloc(sizeof(data_stream_t));

strcpy(stream->name, name);

stream->operators = NULL;

stream->operator_count = 0;

stream->next = env->streams;

env->streams = stream;

env->stream_count++;

pthread_mutex_unlock(&env->mutex);

printf("Flink 创建数据流: %s\n", name);

return stream;

}

// 添加Map算子

operator_t *flink_map(data_stream_t *stream, map_func_t func, const char *name) {

operator_t *op = malloc(sizeof(operator_t));

strcpy(op->name, name);

op->op_type = 0;

op->map_func = func;

op->next = stream->operators;

stream->operators = op;

stream->operator_count++;

printf("Flink 添加Map: %s\n", name);

return op;

}

// 添加Filter算子

operator_t *flink_filter(data_stream_t *stream, filter_func_t func, const char *name) {

operator_t *op = malloc(sizeof(operator_t));

strcpy(op->name, name);

op->op_type = 1;

op->filter_func = func;

op->next = stream->operators;

stream->operators = op;

stream->operator_count++;

printf("Flink 添加Filter: %s\n", name);

return op;

}

```

  1. 窗口实现

```c

// 创建滚动窗口

window_def_t *window_tumbling(long long size_ms) {

window_def_t *w = malloc(sizeof(window_def_t));

w->type = WINDOW_TUMBLING;

w->size_ms = size_ms;

w->slide_ms = size_ms;

w->gap_ms = 0;

return w;

}

// 创建滑动窗口

window_def_t *window_sliding(long long size_ms, long long slide_ms) {

window_def_t *w = malloc(sizeof(window_def_t));

w->type = WINDOW_SLIDING;

w->size_ms = size_ms;

w->slide_ms = slide_ms;

w->gap_ms = 0;

return w;

}

// 窗口聚合

typedef struct window_result {

char key64;

long long window_start;

long long window_end;

long long count;

long long sum;

double avg;

} window_result_t;

// 滚动窗口处理

void process_tumbling_window(event_t **events, int count,

long long window_start, window_result_t *result) {

result->window_start = window_start;

result->window_end = window_start + 10000; // 10秒窗口

result->count = count;

result->sum = 0;

for (int i = 0; i < count; i++) {

result->sum += atol(eventsi->data);

}

result->avg = count > 0 ? (double)result->sum / count : 0;

}

// 执行窗口操作

event_t *flink_window(data_stream_t *stream, window_def_t *w, const char *name) {

printf("Flink 窗口操作: %s (类型: %d)\n", name, w->type);

return NULL;

}

```

  1. 状态管理

```c

// 键控状态

typedef struct keyed_state {

struct state *states;

pthread_mutex_t mutex;

} keyed_state_t;

keyed_state_t *keyed_state_create(void) {

keyed_state_t *ks = malloc(sizeof(keyed_state_t));

ks->states = NULL;

pthread_mutex_init(&ks->mutex, NULL);

return ks;

}

// 获取状态

void *keyed_state_get(keyed_state_t *ks, const char *key) {

pthread_mutex_lock(&ks->mutex);

state_t *s = ks->states;

while (s) {

if (strcmp(s->key, key) == 0) {

pthread_mutex_unlock(&ks->mutex);

return s->value;

}

s = s->next;

}

pthread_mutex_unlock(&ks->mutex);

return NULL;

}

// 更新状态

void keyed_state_put(keyed_state_t *ks, const char *key, void *value, size_t size) {

pthread_mutex_lock(&ks->mutex);

state_t *s = ks->states;

while (s) {

if (strcmp(s->key, key) == 0) {

if (s->value) free(s->value);

s->value = malloc(size);

memcpy(s->value, value, size);

s->value_size = size;

s->last_update = time(NULL);

pthread_mutex_unlock(&ks->mutex);

return;

}

s = s->next;

}

s = malloc(sizeof(state_t));

strcpy(s->key, key);

s->value = malloc(size);

memcpy(s->value, value, size);

s->value_size = size;

s->last_update = time(NULL);

s->next = ks->states;

ks->states = s;

pthread_mutex_unlock(&ks->mutex);

}

```

  1. 测试代码

```c

// 示例Map函数:提取数字

event_t *parse_number_map(event_t *e) {

event_t *out = malloc(sizeof(event_t));

memcpy(out, e, sizeof(event_t));

// 提取数据中的数字

char *p = e->data;

while (*p && !isdigit(*p)) p++;

if (*p) {

char num64;

int i = 0;

while (*p && (isdigit(*p) || *p == '.')) {

numi++ = *p++;

}

numi = '\0';

strcpy(out->data, num);

} else {

strcpy(out->data, "0");

}

return out;

}

// 示例Filter函数:过滤负数

event_t *filter_positive(event_t *e) {

int val = atoi(e->data);

if (val < 0) return NULL;

event_t *out = malloc(sizeof(event_t));

memcpy(out, e, sizeof(event_t));

return out;

}

// 测试流处理

void test_flink() {

printf("=== Flink流处理测试 ===\n\n");

flink_env_t *env = flink_create(4, 5000);

// 创建数据流

data_stream_t *stream = flink_add_stream(env, "number-stream");

// 构建流处理管道

flink_map(stream, parse_number_map, "parse-number");

flink_filter(stream, filter_positive, "filter-positive");

// 添加窗口(10秒滚动窗口)

window_def_t *w = window_tumbling(10000);

flink_window(stream, w, "tumbling-window");

// 模拟数据流

printf("\n模拟数据处理:\n");

char *test_data\[\] = {"data: 100", "data: -50", "data: 200", "data: 150", "data: -30"};

for (int i = 0; i < 5; i++) {

event_t *e = malloc(sizeof(event_t));

strcpy(e->data, test_datai);

e->timestamp = time(NULL) * 1000;

printf(" 输入: %s\n", test_datai);

// 模拟算子执行

event_t *after_map = parse_number_map(e);

printf(" → Map: %s\n", after_map->data);

event_t *after_filter = filter_positive(after_map);

if (after_filter) {

printf(" → Filter: 通过\n");

free(after_filter);

} else {

printf(" → Filter: 过滤\n");

}

free(after_map);

free(e);

}

free(w);

free(env);

}

int main() {

srand(time(NULL));

test_flink();

return 0;

}

```


三、编译和运行

```bash

gcc -o flink flink.c -lpthread -lm

./flink

```


四、Flink vs 本实现

特性 本实现 Flink

流处理 ✅ ✅

窗口 ✅ 基础 ✅ 丰富

状态管理 ✅ ✅

检查点 ❌ ✅

时间语义 ✅ ✅

事件时间 ✅ ✅

水印 ❌ ✅

背压 ❌ ✅


五、总结

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

· Flink的核心架构(JobManager + TaskManager)

· 数据流与算子(Map/Filter/KeyBy)

· 窗口类型(滚动/滑动/会话)

· 状态管理(键控状态)

· 时间语义(Event/Processing Time)

Flink是分布式流处理的经典实现。掌握它,你就理解了实时计算系统的核心设计。

下一篇预告:《从零实现一个分布式任务调度:Apache Airflow的核心设计》


评论区分享一下你用Flink处理过什么实时场景~

相关推荐
2401_869769591 小时前
C/C++常识
c++
j7~2 小时前
【Linux】二十六.线程篇三《Linux多线程编程:线程控制、线程ID以及进程地址空间分布、线程局部存储__thread以及clone系统调用》---详解
linux·运维·服务器·开发语言·c++·多线程编辑·线程的控制
luj_176811 小时前
星火科技助力边远地区防病攻坚
c语言·开发语言·c++·经验分享·算法
zmzb010312 小时前
C++课后习题训练记录Day175
开发语言·c++
啦啦啦啦啦zzzz13 小时前
工具:动态类工厂和用配置文件存储属性
c++·设计模式·工具·动态工厂
脱胎换骨-军哥13 小时前
C++ 代码规范与格式化指南
开发语言·c++·代码规范
迷途之人不知返15 小时前
lambda表达式
c++
@三十一Y15 小时前
C++:红黑树的实现
开发语言·c++
_wyt00115 小时前
拓扑排序:有向无环图的排队艺术
c++·拓扑排序·队列