
Flink车联网远控预警系统实战 —— 第 2 章 技术选型与架构总览章节定位第一篇压轴。第 1 章讲清了业务链路本章搭技术骨架——为什么选 Flink Doris Flink CDC Kafka MySQL DolphinScheduler 这六个组件它们在链路中各自承担什么职责全链路拓扑长什么样。最重要的是亮出本课程贯穿始终的生产硬约束清单这些硬约束会在后续每一章的代码里反复出现。版本基线Apache Flink 2.x Doris 4.x Flink CDC 3.x Kafka 3.x MySQL 8.x DolphinScheduler 3.x章节导读2.1 六大组件选型与职责2.2 全链路拓扑2.3 配置同步方案JDBC 轮询 vs Flink CDC2.4 生产硬约束清单2.5 作业拓扑与模块划分2.6 本章小结与下章预告2.1 六大组件选型与职责本专栏的核心技术组合锁定为六个组件每个组件只做自己最擅长的事职责边界清晰、互不越界。2.1.1 组件职责一览┌────────────────────┬──────────────────────┬──────────────────────────────────┐ │ 组件 │ 角色 │ 在远控预警链路中的职责 │ ├────────────────────┼──────────────────────┼──────────────────────────────────┤ │ Kafka 3.x │ 消息总线 │ 承载 L3 文本日志 L2 JSON 日志 │ │ │ │ 双流输入解耦生产与消费 │ ├────────────────────┼──────────────────────┼──────────────────────────────────┤ │ Flink 2.x │ 流计算引擎 │ 解析、按 requestId 聚合、 │ │ │ │ 结果判定、耗时计算、窗口统计 │ ├────────────────────┼──────────────────────┼──────────────────────────────────┤ │ Flink CDC 3.x │ 变更数据捕获 │ 实时同步 MySQL 预警配置表变更 │ │ │ │ → Flink 广播流配置热更新 │ ├────────────────────┼──────────────────────┼──────────────────────────────────┤ │ MySQL 8.x │ 业务配置库 │ 存 t_cn_remote_warning_config │ │ │ │ 运维在 TSP 后台改配置 │ ├────────────────────┼──────────────────────┼──────────────────────────────────┤ │ Doris 4.x │ 分析型存储 │ DWD 明细表 ADS 预警统计表 │ │ │ │ Unique Key MOW 幂等 upsert │ ├────────────────────┼──────────────────────┼──────────────────────────────────┤ │ DolphinScheduler │ 调度与告警 │ 编排回放/补数作业、作业监控告警、 │ │ 3.x │ │ 失败邮件通知 │ └────────────────────┴──────────────────────┴──────────────────────────────────┘2.1.2 为什么是这六个为什么用 Kafka 而不是直接读 MySQL 日志远控日志是 L3/L2 平台主动推到 Kafka 的,不是数据库 binlog。 Kafka 的作用: ① 解耦: 日志生产方(L3/L2)与消费方(Flink)互不阻塞 ② 缓冲: 高峰期日志洪峰不会直接压垮 Flink ③ 回溯: 消费组位移管理,故障恢复后从断点继续 ④ 多播: 同一份日志可被多个作业消费(本专栏的预警作业 明细作业)为什么用 Flink 而不是 Spark Streaming远控预警的核心诉求是分钟级延迟 精确一次 状态管理: ✗ Spark Streaming 微批本质,延迟在秒级以上,且状态管理不如 Flink 原生 ✓ Flink 原生流处理,毫秒级延迟,Keyed State 定时器天然支持 按 requestId 聚合并等待最终态 这类有状态计算是 Flink 的主场为什么用 Doris 而不是 ClickHouse / HBase预警统计的查询模式: ① 按 config_id sample_time 点查(幂等 upsert 的主键) ② 按时间范围扫预警记录(运维页面) ③ 失败率/超时率聚合分析 Doris 的 Unique Key Merge-on-Write 模型天然支持 upsert, 查询响应在百毫秒级,且支持高并发点查 范围扫描。 HBase 适合 KV 点查但不适合聚合分析;ClickHouse 聚合强但 upsert 弱。为什么用 Flink CDC 同步配置预警配置(阈值、采样区间)存在 MySQL,运维在 TSP 后台随时改。 Flink 作业需要感知这些变更: 方案 A: JDBC 定时轮询(当前实现,60s 一次)—— 延迟高,有幽灵配置问题 方案 B: Flink CDC 监听 binlog —— 变更秒级生效,全量增量一体,Exactly-Once 本课程采用方案 B(第 9 章详解)。为什么用 DolphinSchedulerFlink 实时作业本身是常驻的,但还有: ① 历史数据回放/补数作业(一次性,跑完即停) ② 作业版本发布(灰度、回滚) ③ 作业健康监控(失败告警) DolphinScheduler 统一编排这些任务,失败时邮件通知运维。2.2 全链路拓扑2.2.1 端到端数据流┌──────────────────────────────────────────────────────────────────────────┐ │ 数据源层 │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │ │ │ Kafka L3 Topic │ │ Kafka L2 Topic │ │ MySQL 预警配置表 │ │ │ │ cornex_remote_ │ │ cornex_remote_ │ │ t_cn_remote_warning_ │ │ │ │ log_l3 (文本) │ │ log_l2 (JSON) │ │ config │ │ │ └────────┬────────┘ └────────┬────────┘ └────────────┬────────────┘ │ └───────────┼──────────────────────┼─────────────────────────┼──────────────┘ │ │ │ │ │ ┌────────────▼────────────┐ │ │ │ Flink CDC (binlog) │ │ │ │ → ConfigSnapshot 流 │ │ │ └────────────┬────────────┘ │ │ │ ▼ ▼ ▼ ┌──────────────────────────────────────────────────────────────────────────┐ │ Flink 计算层 │ │ │ │ ┌──────────┐ ┌──────────┐ ┌───────────────┐ ┌──────────────────┐ │ │ │ 1.Source │──▶│ 2.Parse │──▶│ 3.KeyByState │──▶│ 4.结果判定耗时 │ │ │ │ 双流消费 │ │ L3文本/ │ │ requestId 聚合 │ │ 六锚点四公式 │ │ │ │ │ │ L2JSON │ │ 双定时器 │ │ 三级判定 │ │ │ └──────────┘ └──────────┘ └───────┬───────┘ └────────┬─────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────────────────┐ │ │ │ 5. 维表补全(Async I/O) → DWD 明细输出 │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────────┐ │ │ │ 6. 广播配置 分钟桶 整点回溯 样本校验 → ADS 统计输出 │ │ │ └──────────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────┬───────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────────────────┐ │ 存储层 (Doris) │ │ │ │ ┌────────────────────────────┐ ┌────────────────────────────┐ │ │ │ DWD 明细表 │ │ ADS 预警统计表 │ │ │ │ dwd_vehicle_remote_ │ │ ads_vehicle_remote_ │ │ │ │ control_detail │ │ warning_stat │ │ │ │ UK: request_id │ │ UK: config_idsample_time │ │ │ └────────────────────────────┘ └─────────────┬──────────────┘ │ └─────────────────────────────────────────────────┼────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ TSP 应用: 阈值比对 推送 │ │ OA / 邮件通知运维 │ └─────────────────────────────┘ 旁路基建: DolphinScheduler(回放调度告警) / PrometheusGrafana(监控)2.2.2 三层职责边界┌─────────────────────────────────────────────────────────────┐ │ 大数据侧(Flink Doris) —— 只管算得准 │ │ │ │ ① 按 requestId 聚合出单次远控的结果与耗时 │ │ ② 每分钟按配置区间统计失败率/超时率/样本数 │ │ ③ 校验样本数(min_sample_count),不足不写入 │ │ ④ 写入 DWD 明细 ADS 统计 │ │ │ │ ✗ 不做阈值比对 │ │ ✗ 不做推送 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ TSP 应用侧 —— 只管判得对、推得稳 │ │ │ │ ① 拿 ADS 统计记录,与 remote_monitor_frequency 比对 │ │ ② 按 push_alert_value/unit 做推送频率节流 │ │ ③ 按 push_type/push_account 推送 OA / 邮件 │ └─────────────────────────────────────────────────────────────┘为什么阈值判定交给 TSP阈值规则可能频繁调整今天 5%明天 3%如果放在 Flink 作业里每次调整都要重启作业。TSP 侧读配置比对改阈值零延迟生效。大数据侧只保证统计数据准确。2.3 配置同步方案JDBC 轮询 vs Flink CDC预警配置表的同步是本课程的一个关键升级点。当前生产代码用的是 JDBC 轮询本课程将其升级为 Flink CDC。2.3.1 当前实现JDBC 定时轮询// RemoteWarningConfigSource —— 当前生产实现while(running){try(ConnectionconnDriverManager.getConnection(jdbcUrl,...)){ResultSetrsstmt.executeQuery(SELECT id, min_sample_count, sample_interval_value, sample_interval_unit, is_enable FROM t_cn_remote_warning_config);MapString,RemoteWarningConfigmapnewHashMap();while(rs.next()){...map.put(...);}ctx.collect(newConfigSnapshot(map));// 全量快照}catch(Exceptione){log.error(加载失败,沿用上一轮快照);// 失败不挂,保存活}Thread.sleep(refreshSeconds*1000L);// 60s 轮询}优点实现简单不依赖额外组件查询失败沿用上一轮快照Source 不挂痛点痛点 1: 延迟高 运维改了配置,最长要等 60s 才生效。 线上故障时想快速调大阈值止血,这 60s 很致命。 痛点 2: 全量扫描压力 每 60s 全表扫一次,配置表大了之后给 MySQL 造成持续压力。 痛点 3: 幽灵配置风险 轮询是全量替换(clearputAll),配置被删除后下一轮自然消失—— 但如果某一轮查询失败,会沿用上一轮(已删除的)配置, 表现为配置删了但还在生效,直到下一轮查询成功。 痛点 4: 无法感知变更类型 轮询拿到的是全量快照,不知道哪些是新增、哪些是修改、哪些是删除, 无法做精细化的增量处理和变更审计。2.3.2 升级方案Flink CDCMySQL (t_cn_remote_warning_config) │ │ binlog (row 模式) ▼ ┌─────────────────────────┐ │ Flink CDC Source │ │ MySQL-CDC 3.x │ │ · 全量快照(启动时) │ │ · 增量 binlog(运行时) │ │ · Exactly-Once 保证 │ └───────────┬─────────────┘ │ ConfigSnapshot 流 ▼ ┌─────────────────────────┐ │ Flink 广播流 │ │ Broadcast State │ └─────────────────────────┘Flink CDC 的优势维度JDBC 轮询Flink CDC变更延迟60s轮询间隔秒级binlog 实时MySQL 压力每 60s 全表扫描仅读 binlog增量变更感知只有全量快照知道 insert/update/deleteExactly-Once不保证原生支持“幽灵配置”查询失败时存在无此问题依赖仅 JDBC需要 MySQL binlog CDC 连接器Flink CDC 升级的核心代码骨架第 9 章详解// 配置表 CDC Source(Flink CDC 3.x)SourceConfigSnapshot,?,?cdcSourceMySqlSource.ConfigSnapshotbuilder().hostname(mysqlHost).port(3306).databaseList(iov).tableList(t_cn_remote_warning_config).username(cdcUser).password(cdcPassword)// 环境变量注入,禁硬编码.scanNewlyAddedTableEnabled(true).startupOptions(StartupOptions.initial())// 首次全量 后续增量.deserializer(newConfigSnapshotDeserializer()).build();DataStreamConfigSnapshotconfigStreamenv.fromSource(cdcSource,WatermarkStrategy.noWatermarks(),config-cdc);// 广播到下游BroadcastStreamConfigSnapshotbroadcastconfigStream.broadcast(CONFIG_STATE_DESC);2.3.3 升级注意事项纪律: □ MySQL 必须开启 binlog,格式为 ROW,且 binlog_row_imageFULL □ CDC 账号仅授 SELECT RELOAD SHOW DATABASES REPLICATION SLAVE REPLICATION CLIENT □ 连接器 jar 放入 $FLINK_HOME/lib,禁 -j 参数(前作硬约束) □ 密码走环境变量/K8s Secret,不硬编码进 DDL/代码 □ 首次启动走 initial 快照,存量数据全量同步;之后走 binlog 增量 □ 上线前先 curl/连通性测试验证 MySQL 可达2.4 生产硬约束清单这是本课程最重要的一节。下面这些硬约束会在后续每一章的代码里反复出现没有商量余地。2.4.1 Flink 作业级硬约束#硬约束说明违反后果1jar 包进$FLINK_HOME/lib提交时禁止-j参数双 ClassLoader 导致not a subtype错误2-D pipeline.name命名WebUI 识别作业作业列表里找不到自己的作业3Checkpoint 间隔 30sPaimon Sink 依赖 Checkpoint 提交数据不可见4Checkpoint 模式 Exactly-Once端到端精确一次数据重复或丢失5Unaligned Checkpoint反压时加速 CheckpointCheckpoint 超时 → 作业重启6重启策略 fixed-delay 无限重试attemptsInteger.MAX_VALUE作业挂了不自动恢复7ForSt 状态后端大状态窗口支撑RocksDB 已废弃8State TTL 48h聚合/ 1h维表缓存防止状态膨胀OOM9Kafkagroup.id配置化从配置文件读环境冲突10Kafkascan.startup.modegroup-offsets生产环境用消费组位移重启后重消费全量数据2.4.2 Kafka 硬约束□ 消费组 ID 从配置文件读取,不硬编码 □ 生产环境 scan.startup.mode group-offsets(不用 earliest-offset) □ 隔离环境用不同 group.id,避免互相消费 □ 超时与重试参数配置化2.4.3 Doris 硬约束□ 表模型: Unique Key Merge-on-Write(enable_unique_key_merge_on_writetrue) □ DATETIME 字段用 yyyy-MM-dd HH:mm:ss 格式,不带毫秒/纳秒 □ 金额/比率字段用 DECIMAL,不用 DOUBLE □ Stream Load 写入,label 前缀业务化(如 remote_warning_) □ 敏感字段(车主信息)写入前脱敏2.4.4 时间与时区硬约束□ L3 时间从日志原文前 23 字符提取(yyyy-MM-dd HH:mm:ss.SSS),不用 ES timestamp □ L2 时间用 JSON 的 time 字段(epoch 毫秒) □ 两侧都转本地毫秒后相减,时区偏移自动抵消 □ TaskManager JVM 时区必须是 Asia/Shanghai → 否则分钟分桶与 sample_time 整体偏 8 小时(静默错误,最难排查) □ 毫秒时间戳存库时 /1000 转秒级(Doris DATETIME 不支持毫秒)2.4.5 安全硬约束□ 所有密钥(LLM/MySQL/Kafka/Doris)走环境变量/K8s Secret,禁硬编码 □ envsubst 渲染部署文件,Git 仓库零明文 □ 凭证 90 天轮换,轮换期间双凭证并行 □ 动作类工具必须幂等(event_id 去重键)2.4.6 硬约束的设计哲学这些约束不是最佳实践的建议,而是踩过坑之后的教训: · jar 不进 lib → 提交报 ClassCastException,排查半天 · 不用 group-offsets → 重启后从最早消费,把历史数据全跑一遍 · JVM 时区不对 → sample_time 偏 8 小时,数据全错但不报错 · DATETIME 带毫秒 → Doris 写入报格式错误 把这些约束钉死,后面写代码时就不会再踩同样的坑。2.5 作业拓扑与模块划分2.5.1 三个 Flink 作业┌─────────────────────────────────────────────────────────────┐ │ 作业 1: RemoteWarningStatJob (实时主作业) │ │ · 常驻运行,消费 Kafka L3/L2 双流 │ │ · 解析 → 聚合 → 判定 → DWD → 窗口统计 → ADS │ │ · Checkpoint 30s, fixed-delay 无限重试 │ └─────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ │ 作业 2: RemoteWarningBackfillJob (回放/补数作业) │ │ · 由 DolphinScheduler 触发,跑完即停 │ │ · 读历史 Kafka 数据,按数据时间驱动统计 │ │ · 分批推进(每批 120 分钟),断点续补 │ └─────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ │ 作业 3: RemoteWarningMockJob (本地造数/自测作业) │ │ · 开发自测用,5s 触发、1 分钟宽限 │ │ · 构造测试数据验证聚合口径与输出正确性 │ └─────────────────────────────────────────────────────────────┘2.5.2 代码模块结构auto-vehicle-flink-ods/src/main/java/com/cornexauto/car/realtime/remote/ ├── app/ │ ├── RemoteWarningStatJob.java # 实时主作业入口 │ ├── RemoteWarningBackfillJob.java # 回放作业入口 │ └── RemoteWarningMockJob.java # 自测作业入口 ├── function/ │ ├── L3TextParseProcessFunction.java # L3 文本解析 │ ├── L2JsonParseProcessFunction.java # L2 JSON 解析 │ ├── RemoteControlAggregateKeyedProcessFunction.java # requestId 聚合 │ ├── RemoteWarningStatFunction.java # 广播窗口统计 │ └── RemoteWarningConfigSource.java # 配置 Source(JDBC 轮询,待升级 CDC) ├── model/ │ ├── CtrlNode.java # 解析后统一节点 │ ├── RemoteControlRecord.java # 聚合后单次远控记录 │ ├── RemoteWarningConfig.java # 预警配置 │ ├── ConfigSnapshot.java # 配置快照 │ ├── MinuteBucket.java # 分钟桶 │ └── RemoteWarningStat.java # ADS 输出行 └── tools/ └── (测试工具类)2.5.3 状态与数据流转Kafka L3 ──┐ ├─ union ── keyBy(requestId) ──▶ AggState(6 锚点) ──▶ RemoteControlRecord Kafka L2 ──┘ │ ▼ Doris DWD (request_id UK) │ MySQL 配置 ── Flink CDC ──▶ BroadcastStream ──┐ │ ▼ ▼ RemoteWarningStatFunction (分钟桶 整点回溯 样本校验) │ ▼ Doris ADS (config_idsample_time UK)2.6 本章小结与下章预告本章小结┌──────────────────────────────────────────────────────────────┐ │ 第 2 章 要点回顾 │ └──────────────────────────────────────────────────────────────┘ ✓ 六大组件: Kafka(消息) / Flink(计算) / Flink CDC(配置同步) / MySQL(配置库) / Doris(存储) / DolphinScheduler(调度) 各管一段,职责不越界 ✓ 全链路: Kafka 双流 → Flink 解析聚合 → DWD 明细 → 广播窗口统计 → ADS 统计 → TSP 阈值判定推送 ✓ 职责边界: 大数据侧只管算得准(统计样本校验), TSP 侧管判得对、推得稳(阈值节流推送) ✓ 配置同步: JDBC 轮询(60s,有幽灵配置风险) → Flink CDC(秒级,binlog 增量,Exactly-Once) ✓ 硬约束: jar 进 lib / pipeline.name / Checkpoint 30s / fixed-delay 无限重试 / ForSt / group-offsets / Doris UKMOW / DATETIME 秒级 / JVM 时区 Asia/Shanghai ✓ 三作业: 实时主作业(常驻) / 回放作业(DS 触发) / 自测作业(本地验证)下章预告第 3 章 Kafka 双流接入L3 文本 L2 JSON骨架搭好了开始写代码。本章从数据源入手——怎么消费 Kafka 的 L3 文本日志和 L2 JSON 日志怎么配置消费组和 Watermark怎么把格式完全不同的两条流 union 成统一的CtrlNode模型。这是整个计算链路的起点解析对了后面才不会错。官方参考资料Apache Flink DocumentationApache Doris DocumentationFlink CDC DocumentationApache Kafka DocumentationDolphinScheduler Documentation本章实战代码清单序号内容说明1六大组件职责与选型理由2.12全链路拓扑图数据流 职责边界2.23JDBC 轮询 vs Flink CDC 对比与升级代码骨架2.34生产硬约束清单Flink/Kafka/Doris/时区/安全2.45三作业划分与代码模块结构2.5