Flink车联网远控预警系统实战 —— 第 6 章 业务规则引擎:耗时计算与失败判定
Flink车联网远控预警系统实战 —— 第 6 章 业务规则引擎耗时计算与失败判定章节定位第三篇收尾。第 1 章讲了业务规则六锚点四公式、三级判定第 5 章讲了代码实现AggState、双定时器。本章把两者对照起来——聚合层实际计算了哪些耗时、超时怎么判、失败判定的所有边界场景、节点缺失的连锁影响。这是业务规则与代码实现的桥梁也是排查线上问题时最常翻的一章。版本基线Apache Flink 2.x章节导读6.1 聚合层实际计算的耗时6.2 超时判定的精确口径6.3 三级失败判定全景6.4 节点缺失的影响矩阵6.5 典型场景判定结果速查6.6 阈值外置与口径对齐6.7 本章小结与下章预告6.1 聚合层实际计算的耗时第 1 章讲了四段耗时ctrlTime/l3Time/tboxTime/l2Time但聚合层实际只计算了一段。6.1.1 只算 ctrlTime// buildRecord 方法LongctrlTimestate.endMs!null?state.endMs-state.startMs:null;聚合层只计算 ctrlTime(总耗时),用于超时判定。 为什么不算 l3Time/tboxTime/l2Time? 预警统计只需要失败率和超时率,不需要分段耗时。 分段耗时是远控明细查询作业的职责(给运维页面看各环节耗时), 那个作业用同一套锚点计算四段耗时,但不做预警统计。 职责分离: 预警作业 → 算 ctrlTime → 判定超时 → 统计失败率/超时率 明细作业 → 算四段耗时 → 写 DWD 明细表 → 页面查询6.1.2 ctrlTime 的取值规则endMsstartMsctrlTime说明有值有值endMs - startMs正常计算null有值null无最终态,无法计算总耗时有值null——startMs 缺失,整条丢弃,不输出ctrlTime 为 null 的场景: · L3_TO_APP 最终态丢失(兜底输出) · 只有中间态指令已下发,没有最终态 ctrlTime 为 null 时: · 不计超时(timeoutfalse) · 但仍可能判失败(如车机结果明确非1)6.1.3 与第 1 章四段耗时的关系第 1 章讲的四段耗时是远控明细查询作业的计算逻辑: ctrlTime end - start l3Time (firstL3ToL2 - start) (end - lastL2ToL3) tboxTime fl0 - tl0 l2Time ctrlTime - l3Time - tboxTime(负数清零) 本预警作业只用 ctrlTime: · 锚点都存在 AggState 里,但只用于 ctrlTime · l3Time/tboxTime/l2Time 在明细作业计算 · 两个作业共享同一套解析和聚合逻辑,只是输出不同6.2 超时判定的精确口径6.2.1 超时公式booleantimeoutfailctrlTime!nullctrlTimebusinessTimeoutMs;6.2.2 三个必要条件超时 ① 失败 ② 有总耗时 ③ 总耗时 30s 三个条件缺一不可: ① 成功不超时(成功了就不叫超时) ② ctrlTimenull 不超时(不知道耗时多久) ③ ctrlTime30s 不超时(30s 内失败叫快速失败,不叫超时)6.2.3 超时是失败的子集failtrue 的所有记录中: ├── ctrlTimenull → timeoutfalse(兜底失败) ├── ctrlTime30000 → timeoutfalse(快速失败) └── ctrlTime30000 → timeouttrue(超时) 统计关系: remote_timeout_count ≤ remote_fail_count remote_timeout_rate ≤ remote_fail_rate6.2.4 为什么超时必须同时是失败场景: 远控耗时 35s,但最终成功了。 如果只按耗时判超时,这条会被计入 timeout。 但业务上35s 后成功不算故障,只是慢。 用户已经拿到结果了,不应该告警。 所以超时 失败 耗时过长。 只有失败且慢才需要运维介入。6.3 三级失败判定全景第 5 章讲了判定代码本章用表格把所有边界场景列全。6.3.1 判定优先级优先级: L3 最终态 车机结果 L2 结果(主??辅) 高优先级有结论 → 直接用,不看低优先级 高优先级无结论(null) → 往下查 所有层级都无明确失败结论 → 未知6.3.2 完整判定表ctrlResulttboxResultl2Result(main??aux)failfailSourcectrlResult(输出)“1”任意任意falsenull“1”“0”任意任意trueL3“0”null“0”任意trueTBOX“0”null“1”“0”trueL2“0”null“1”“1”falsenullnullnull“1”nullfalsenullnullnullnull“0”trueL2“0”nullnull“1”falsenullnullnullnullnullfalsenullnulll2Result(main??aux)表示主来源L2_TO_L3优先主来源为 null 时用辅助来源T_L3。6.3.3 关键边界说明边界 1: ctrlResult1 时,即使 tboxResult0 也判成功 因为 L3 最终态是最高优先级,L3 说成功就是成功。 车机可能先失败后重试成功,L3 汇总后给的是最终结论。 边界 2: 车机和 L2 都失败,定位到 TBOX(下游优先) ctrlResultnull, tboxResult0, l2Result0 → failSourceTBOX,因为车机是最下游,根因在车机。 边界 3: 都成功或都缺失 → 未知(ctrlResultnull) 注意: 未知不是失败,不计入 fail_count。 但计入 sample_count(分母)。 边界 4: 只有 L2 消息,无 APP_TO_L3 → 整条丢弃 startMsnull,buildRecord 返回 null,不输出。6.4 节点缺失的影响矩阵远控链路中任何一个节点可能缺失对输出字段的影响如下。6.4.1 影响矩阵缺失节点影响锚点ctrlTimefailfailSourcetimeout说明APP_TO_L3startMs————————整条丢弃无法归属采样区间L3_TO_APP 最终态endMsnull视兜底视兜底false走兜底ctrlTimenull 不计超时L3_TO_L2firstL3ToL2不影响不影响不影响不影响本作业不用此锚点L2_TO_L3lastL2ToL3不影响l2ResultMain 缺失可能变未知不影响失去 L2 主结果来源T_L0tl0不影响不影响不影响不影响本作业不用此锚点F_L0fl0/tboxResult不影响tboxResult 缺失可能变未知不影响失去车机结果来源6.4.2 最大影响L3_TO_APP 缺失L3_TO_APP 最终态缺失时: endMs null → ctrlTime null ctrlResult null → 走兜底判定 可能的结果: · 有 F_L0 且 tboxResult0 → failtrue, failSourceTBOX, timeoutfalse · 有 L2_TO_L3 且 l2Result0 → failtrue, failSourceL2, timeoutfalse · 都成功或都缺失 → failfalse(未知), timeoutfalse 共同特征: timeout 一定是 false(因为 ctrlTimenull)。6.4.3 最小影响L3_TO_L2 / T_L0 缺失L3_TO_L2 和 T_L0 缺失对预警统计完全无影响: · firstL3ToL2 用于 l3Time 计算,本作业不用 · tl0 用于 tboxTime 计算,本作业不用 · 不影响失败判定,不影响超时判定 但在远控明细查询作业中,这两个节点缺失会导致 l3Time 前段0、tboxTime0,需要前端显示-。6.5 典型场景判定结果速查这张表是排查线上问题时最常用的对照输入节点就能知道输出。场景ctrlResulttboxResultl2ResultctrlTimefailfailSourcetimeout全链路成功“1”“1”“1”5000falsenullfalse车机执行失败“0”“0”“1”35000trueL3trueL2 通信超时“0”“1”“0”35000trueL3trueL3 组装失败“0”“1”“1”8000trueL3false无最终态车机失败null“0”“1”nulltrueTBOXfalse无最终态L2失败null“1”“0”nulltrueL2false无最终态都成功null“1”“1”nullfalsenullfalse无最终态都缺失nullnullnullnullfalsenullfalse只有中间态null“1”“1”nullfalsenullfalse无 APP_TO_L3———————最后一行无 APP_TO_L3整条丢弃不输出记录所以所有字段都是—。6.5.1 特别注意L3 失败时 failSource 总是 L3看表中车机执行失败和L2 通信超时两行: 虽然根因在车机或 L2,但 ctrlResult0(L3 最终态), 所以 failSourceL3。 这是设计取舍: · L3 最终态是最高优先级,有 L3 结论就用 L3 的 · failSource 表示判定来源,不是根因来源 · 要找根因,需要看 tboxResult/l2Result 等中间结果 如果需要根因定位,应在明细作业里分析各层结果码, 预警作业只需要知道失败了和超时了。6.6 阈值外置与口径对齐6.6.1 超时阈值外置// 构造函数传入publicRemoteControlAggregateKeyedProcessFunction(longfinalDelayMs,longaggregateTimeoutMs,longbusinessTimeoutMs,longstateTtlMinutes)// Job 侧配置longbusinessTimeoutMsconfig.getLong(remote.business.timeout.ms,30000L);6.6.2 为什么阈值要外置阈值硬编码的问题: · 改阈值要改代码 → 编译 → 打包 → 重启作业 · 不同环境(测试/生产)阈值可能不同 · 业务方想临时调阈值止血,走流程要半天 阈值外置的好处: · 改配置即可,不用改代码 · 配置文件随环境走 · 紧急情况下运维直接改配置重启 但注意: 改阈值会影响判定口径,必须与 TSP 对齐。 如果大数据侧 30s 判超时,TSP 侧用 60s 判超时, 两侧数据会不一致。6.6.3 与 TSP 口径对齐大数据侧判定超时: ctrlTime 30000 fail TSP 侧判定超时: 从 ADS 表读 remote_timeout_count(已由大数据算好) 对齐要求: □ 大数据侧和 TSP 侧使用同一个超时阈值(30000ms) □ 阈值变更时,两侧同时变更 □ 变更记录留档(谁/何时/为什么) 如果 TSP 想在应用层再做二次超时判定, 必须明确告知大数据侧,避免重复统计或口径冲突。6.7 本章小结与下章预告本章小结┌──────────────────────────────────────────────────────────────┐ │ 第 6 章 要点回顾 │ └──────────────────────────────────────────────────────────────┘ ✓ 聚合层只算 ctrlTime(总耗时),用于超时判定; l3Time/tboxTime/l2Time 由明细作业计算 ✓ 超时三条件: fail ctrlTime!null ctrlTime30s; 超时是失败的子集,成功不超时 ✓ 三级判定: L3最终态 车机 L2(主??辅); 高优先级有结论直接用,都无明确失败为未知(不计失败) ✓ 节点缺失影响: APP_TO_L3 缺失整条丢弃; L3_TO_APP 缺失 ctrlTimenull 不计超时; L3_TO_L2/T_L0 缺失对预警无影响 ✓ 阈值外置: remote.business.timeout.ms30000, 改配置不改代码;变更时与 TSP 对齐口径 ✓ failSource 是判定来源不是根因来源, 有 L3 最终态时总是 L3下章预告第 7 章 维表补全Async I/O 缓存远控日志只有 VIN需要补全品牌/车系/车型等维表信息。本章讲用 Async I/O 查询车辆维表 Caffeine 缓存以及维表查询失败时的降级策略——留空写入绝不阻塞主流。官方参考资料Apache Flink Async I/O本章实战代码清单序号内容说明1ctrlTime 计算与取值规则6.12超时判定三条件fail ctrlTime 30s6.23三级失败判定完整场景表6.34节点缺失影响矩阵6.45典型场景判定结果速查表6.56超时阈值外置与 TSP 口径对齐6.6