1、它解决什么问题?
FailureEnricher 的典型用途:
- 故障分类/归因 :给失败贴上
type=System、type=User、component=Kafka、reason=AuthFailed等标签 - 告警路由:不同标签推送到不同 oncall(平台组/业务组/中间件组)
- 可观测性增强:把标签送到外部系统(Prometheus/OTel/自研平台)做统计报表
- 联动通知 :在
processFailure()里调用外部通知系统(注意:建议异步且要兜底超时)
触发时机:
- 每次 JobManager 在运行时收到异常报告时都会触发 FailureEnrichers
- Enricher 可以 异步 返回 labels(
CompletableFuture<Map<String,String>>)
2、插件结构与加载方式(重点)
2.1 你要实现的 3 件事
- 实现
FailureEnricher - 实现
FailureEnricherFactory - 使用 Java SPI 注册工厂:
META-INF/services/org.apache.flink.core.failure.FailureEnricherFactory
然后把 jar 放到 Flink plugins/ 下的某个目录,例如:
text
$FLINK_HOME/plugins/
failure-enrichment/
my-failure-enricher.jar
2.2 关键约束:output keys 必须唯一
每个 FailureEnricher 都要声明自己可能输出的 key 集合(getOutputKeys())。
所有 enrichers 的 key 集合必须互不重叠 ,否则有重叠 key 的 enrichers 会被忽略 (直接不生效)。
实践建议:统一加前缀,例如 fe. 或 org.:
fe.typefe.componentfe.ownerfe.ticket
3、最小可用示例(可直接改成你们的规则)
3.1 Factory
java
public class CustomFailureEnricherFactory implements FailureEnricherFactory {
@Override
public FailureEnricher createFailureEnricher(Configuration conf) {
return new CustomEnricher(conf);
}
}
3.2 Enricher(示例:按异常类型简单分类)
java
public class CustomEnricher implements FailureEnricher {
private final Set<String> outputKeys;
public CustomEnricher(Configuration conf) {
this.outputKeys = Set.of("fe.type", "fe.component");
}
@Override
public Set<String> getOutputKeys() {
return outputKeys;
}
@Override
public CompletableFuture<Map<String, String>> processFailure(Throwable cause, Context context) {
return CompletableFuture.supplyAsync(() -> {
Map<String, String> labels = new HashMap<>();
String msg = String.valueOf(cause.getMessage());
labels.put("fe.type", "System");
if (msg.contains("org.apache.kafka") || msg.contains("Sasl") || msg.contains("Kafka")) {
labels.put("fe.component", "Kafka");
} else if (msg.contains("org.apache.hadoop") || msg.contains("hdfs")) {
labels.put("fe.component", "HDFS");
} else {
labels.put("fe.component", "Unknown");
}
return labels;
});
}
}
生产建议:这里的异步线程池别用默认 ForkJoinPool,最好用你自己可控的 executor,并设置超时/熔断,避免 enrichment 本身成为压力源。
3.3 SPI 文件内容
文件路径:
text
META-INF/services/org.apache.flink.core.failure.FailureEnricherFactory
文件内容(一行一个工厂类名):
text
com.yourcompany.flink.failure.CustomFailureEnricherFactory
4、配置:不配就不会启动
JobManager 启动时加载 FailureEnricher 插件,但 是否启用由配置决定:
properties
jobmanager.failure-enrichers = com.yourcompany.flink.failure.CustomEnricher
注意点:
- 如果
jobmanager.failure-enrichers为空:不会启动任何 enricher - 配置的是 FailureEnricher 的类名(按你给的示例是这样写的),确保与实际类一致
5、如何验证它是否生效?
5.1 看 JobManager 日志
启动时会出现类似日志:
text
Found failure enricher com.xxx.CustomEnricher at jar:file:/path/to/flink/plugins/failure-enrichment/xxx.jar!/...
5.2 看 REST API 输出的 failureLabels
从 JobManager REST API 查询时,会出现:
json
"failureLabels": {
"fe.type": "System",
"fe.component": "Kafka"
}
你们可以用这个字段做:
- 告警系统的规则匹配
- 失败原因的聚合统计(按 label 分组)
- 自助排障页的"失败类型"展示
6、生产落地建议(少踩坑)
- label key 规范化:统一前缀 + 固定枚举值,避免团队各写各的导致不可聚合
- 异步要可控:线程池、超时、异常兜底(enricher 失败不应影响主流程)
- 避免重 IO/外部依赖:如要调用外部系统,务必做熔断/缓存/降级(否则故障时雪上加霜)
- 和 Metrics/Event/Trace 打通 :failureLabels 很适合与 structured logging 的
flink-job-id一起作为关联维度