3、精算数据流接入:Kafka数据源配置、JSON/AVRO格式解析、Schema Registry集成、数据质量校验

精算坏样本归因的首要前提是稳定、可靠地接入上游业务系统的保单、理赔、核保等数据。本章聚焦于如何利用 Flink 连接 Kafka 作为数据源,处理 JSON 与 AVRO 两种主流序列化格式,集成 Schema Registry 实现模式管理,并在数据进入计算管道前完成质量校验。

3.1 Kafka 数据源配置

Flink 通过 FlinkKafkaConsumer(旧版 API)或 KafkaSource(新版 API)接入 Kafka 主题。精算场景下,建议使用 KafkaSource 以获得更好的 exactly-once 语义与动态分区发现能力。

配置项 推荐值 说明
bootstrap.servers kafka1:9092,kafka2:9092 生产环境至少 2 个 broker
group.id flink-actuary-group 消费者组,用于 offset 管理
auto.offset.reset earliest 首次启动或 offset 丢失时从头消费
enable.auto.commit false 由 Flink checkpoint 控制提交
partition.discovery.interval.ms 60000 每分钟检查新分区
精算实践提示: 坏样本归因通常需要回溯历史数据,建议将 auto.offset.reset 设为 earliest,并在首次部署时手动指定起始 offset 或时间戳,避免遗漏关键样本。
// Flink 1.17+ KafkaSource 配置示例
KafkaSource<String> source = KafkaSource.<String>builder()
    .setBootstrapServers("kafka1:9092,kafka2:9092")
    .setTopics("actuary.claim.raw", "actuary.policy.raw")
    .setGroupId("flink-actuary-group")
    .setStartingOffsets(OffsetsInitializer.earliest())
    .setProperties(immutableMap.of(
        "enable.auto.commit", "false",
        "partition.discovery.interval.ms", "60000"
    ))
    .build();

3.2 JSON 与 AVRO 格式解析

精算数据流中,JSON 常用于非结构化字段(如理赔描述、核保备注),AVRO 则用于结构化保单、费率表等强类型数据。Flink 需要根据数据格式选择对应的反序列化器。

3.2.1 JSON 解析

使用 SimpleStringSchema 接收原始字符串后,通过 JsonDeserializationSchema 或自定义 ObjectMapper 转换为 POJO。精算字段中常见嵌套 JSON,建议使用 JSONPath 提取关键指标。

// 自定义 JSON 反序列化
DataStream<ClaimEvent> claimStream = env
    .fromSource(kafkaSource, WatermarkStrategy.noWatermarks(), "Kafka Source")
    .map(jsonStr -> {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode root = mapper.readTree(jsonStr);
        return new ClaimEvent(
            root.path("claimId").asText(),
            root.path("policyNo").asText(),
            root.path("lossAmount").asDouble(),
            root.path("occurDate").asText()
        );
    });

3.2.2 AVRO 解析

AVRO 需要预定义 Schema,Flink 提供 AvroDeserializationSchema 或 ConfluentRegistryAvroDeserializationSchema。精算团队应提前将 AVRO Schema 注册到 Schema Registry,避免硬编码。

// AVRO 反序列化(使用静态 Schema)
Schema schema = new Schema.Parser().parse(
    getClass().getResourceAsStream("/avro/PolicyEvent.avsc")
);
DataStream<PolicyEvent> policyStream = env
    .fromSource(kafkaSource, WatermarkStrategy.noWatermarks(), "AVRO Source")
    .map(new AvroDeserializationSchema<PolicyEvent>(PolicyEvent.class));
格式选择建议: 对于精算坏样本归因,建议核心指标字段(如保额、赔付率、风险评分)使用 AVRO 保证类型安全;非结构化备注、标签字段使用 JSON 保持灵活性。混合使用时,可在 Kafka 消息头标记 content-type。

3.3 Schema Registry 集成

Schema Registry 是精算数据治理的核心组件,确保生产者和消费者使用兼容的 Schema。Flink 通过 Confluent 提供的 KafkaAvroDeserializer 集成 Registry,支持向前/向后兼容。

兼容性类型 说明 精算场景适用性
BACKWARD 新 Schema 可以读取旧数据 新增精算字段时推荐
FORWARD 旧 Schema 可以读取新数据 删除废弃字段时使用
FULL 双向兼容 核心精算表(如费率表)强制要求
NONE 不校验 仅用于临时调试
// 集成 Schema Registry 的 AVRO 反序列化
Properties props = new Properties();
props.setProperty("bootstrap.servers", "kafka1:9092");
props.setProperty("schema.registry.url", "http://schema-registry:8081");
props.setProperty("specific.avro.reader", "true");

KafkaSource<PolicyEvent> avroSource = KafkaSource.<PolicyEvent>builder()
    .setBootstrapServers("kafka1:9092")
    .setTopics("actuary.policy.avro")
    .setGroupId("flink-actuary-group")
    .setDeserializer(
        ConfluentRegistryAvroDeserializationSchema.forSpecific(PolicyEvent.class, PolicyEvent.class, props)
    )
    .build();
常见陷阱: Schema Registry 的 URL 必须所有 Flink TaskManager 均可访问;如果使用 Docker/K8s 部署,建议使用内部服务名而非 localhost。另外,精算 Schema 变更需走审批流程,避免生产环境出现兼容性断裂。

3.4 数据质量校验

坏样本归因对数据质量高度敏感——缺失的保额、异常的赔付率、错误的日期格式都会导致归因偏差。Flink 管道中应在 source 之后立即嵌入质量校验算子。

3.4.1 校验维度

  • 完整性: 关键字段(claimId、policyNo、lossAmount)非空
  • 准确性: 数值范围校验(如 lossAmount > 0 且 < 1e8)
  • 一致性: 日期格式匹配 yyyy-MM-dd,枚举值在允许列表中
  • 时效性: 事件时间戳不能晚于当前时间 + 1 小时(允许时钟偏差)

3.4.2 校验实现

// 数据质量校验算子示例
DataStream<ClaimEvent> validatedStream = claimStream
    .filter(event -> {
        if (event.getClaimId() == null || event.getClaimId().isEmpty()) {
            Metrics.counter("dq.missing.claimId").inc();
            return false;
        }
        if (event.getLossAmount() <= 0 || event.getLossAmount() > 1e8) {
            Metrics.counter("dq.invalid.lossAmount").inc();
            return false;
        }
        // 日期格式校验
        try {
            LocalDate.parse(event.getOccurDate(), DateTimeFormatter.ISO_LOCAL_DATE);
        } catch (Exception e) {
            Metrics.counter("dq.bad.date").inc();
            return false;
        }
        return true;
    })
    .name("DataQualityFilter");

3.4.3 异常数据路由

校验失败的数据不应直接丢弃,应路由到侧输出(Side Output)或死信队列(DLQ),供精算团队事后分析。

// 侧输出收集异常数据
OutputTag<ClaimEvent> dqTag = new OutputTag<ClaimEvent>("dq-failed") {};
SingleOutputStreamOperator<ClaimEvent> mainStream = validatedStream
    .process(new ProcessFunction<ClaimEvent, ClaimEvent>() {
        @Override
        public void processElement(ClaimEvent value, Context ctx, Collector<ClaimEvent> out) {
            if (passesAllChecks(value)) {
                out.collect(value);
            } else {
                ctx.output(dqTag, value);
            }
        }
    });

DataStream<ClaimEvent> dqFailedStream = mainStream.getSideOutput(dqTag);
dqFailedStream.addSink(new KafkaSink<>(...)); // 写入 DLQ 主题
精算数据流接入与质量校验架构 Kafka 集群 actuary.claim.raw Flink KafkaSource 分区发现 + offset 管理 格式解析 JSON / AVRO 反序列化 Schema Registry 集成 Schema Registry 兼容性校验 数据质量校验 完整性 / 准确性 / 一致性 时效性检查 死信队列 (DLQ) 异常数据路由 清洗后数据流 精算校验要点 保额 > 0 日期格式 yyyy-MM-dd 枚举值白名单

3.5 本章小结