3. 数据源接入:常见实时数据源对比与选型

实时特征生产的第一步是稳定、低延迟地接入数据源。消息队列作为数据管道核心组件,其选型直接决定链路的吞吐、延迟与运维复杂度。本章从主流消息队列对比、选型原则、数据格式三个维度展开。

3.1 主流实时数据源对比:Kafka vs. Kinesis vs. Pulsar

当前工业界最常用的实时数据源为 Apache Kafka、Amazon Kinesis 和 Apache Pulsar。三者均支持发布-订阅模型,但在架构、性能、运维上存在显著差异。

对比维度 Apache Kafka Amazon Kinesis Apache Pulsar
架构模型 分区日志 + 消费者组 Shard 流 + 消费者 分层架构(BookKeeper 存储 + Broker 计算)
存储与计算 耦合(Broker 同时负责存储与计算) 托管服务,存储与计算由 AWS 管理 分离(Broker 无状态,BookKeeper 负责持久化)
消息保留 基于时间/大小策略,可配置 默认 24 小时,最长 365 天 基于时间/大小/TTL,支持分层存储
吞吐量 高(百万级 msg/s,依赖分区数) 中高(单 Shard 1MB/s 写入,2MB/s 读取) 高(水平扩展 Broker 与 BookKeeper)
端到端延迟 毫秒级(通常 5-20ms) 百毫秒级(通常 200-1000ms) 毫秒级(通常 5-15ms)
消息顺序 分区内严格有序 Shard 内严格有序 分区内严格有序
重播能力 支持(基于 Offset 重置) 支持(基于 Shard Iterator) 支持(基于 Cursor 管理)
运维复杂度 高(需管理 ZooKeeper、Broker、磁盘) 低(全托管,无需运维) 中(需管理 Broker、BookKeeper 集群)
成本模型 自建:硬件 + 运维人力;托管:按集群规格 按 Shard 小时 + PUT/GET 请求量 自建:硬件 + 运维人力;托管:按集群规格
适用场景 高吞吐、自建、强一致性、复杂 ETL AWS 生态、无服务器、快速接入 多租户、高可用、跨地域复制、云原生
选型速判: 如果团队已有 Kafka 运维能力且追求极致延迟,选 Kafka;如果业务完全运行在 AWS 且希望减少运维,选 Kinesis;如果面临多租户隔离、跨地域复制或需要存储与计算分离架构,选 Pulsar。

3.2 消息队列选型原则

在实时特征生产场景下,消息队列选型需遵循以下五项核心原则:

  1. 延迟优先: 特征生产对端到端延迟敏感(通常要求 < 100ms),优先选择毫秒级延迟的消息队列。
  2. 吞吐与扩展性: 根据峰值流量预估分区/Shard 数,确保队列可水平扩展,避免单点瓶颈。
  3. 消息可靠性: 至少一次语义(At-Least-Once)是底线;关键特征链路需支持 Exactly-Once 语义。
  4. 重播与回溯: 模型训练需要历史数据回溯,队列必须支持按时间或 Offset 重播。
  5. 运维成本: 自建 vs. 托管需权衡。小团队优先托管服务,大团队可自建以降低成本。
避坑提示: 不要盲目追求“全托管”而忽略延迟要求。Kinesis 在低吞吐场景下延迟可达 1 秒以上,不适合毫秒级特征生产。反之,Kafka 在分区数过多时(> 1000)可能引发 Rebalance 风暴,需提前规划分区策略。

3.3 数据格式选择:Avro vs. Protobuf vs. JSON

消息队列中传输的数据格式直接影响序列化/反序列化性能、Schema 演进能力与可读性。三种主流格式对比如下:

对比维度 Avro Protobuf JSON
序列化方式 二进制(自带 Schema) 二进制(需 .proto 定义) 文本(UTF-8)
Schema 管理 Schema Registry 强依赖 可配合 Registry 或内嵌 无强制 Schema
数据体积 小(比 JSON 小 60-80%) 小(比 JSON 小 50-70%) 大(冗余字段名)
序列化/反序列化速度 快(零拷贝支持) 快(C++/Java 原生实现) 慢(需解析字符串)
Schema 演进 支持(向前/向后兼容) 支持(需遵循兼容性规则) 弱(需手动处理字段变更)
可读性 差(二进制,需工具解码) 差(二进制,需工具解码) 好(人类可读)
生态集成 Kafka、Flink、Spark 原生支持 Kafka、gRPC、Flink 支持 所有语言/工具原生支持
适用场景 大数据生态、Schema 频繁演进 微服务间 RPC、高性能场景 调试、低吞吐、Schema 稳定
生产建议: 实时特征生产链路中,强烈推荐使用 Avro + Schema Registry。原因:Avro 体积小、序列化快,且 Schema Registry 能保证生产者和消费者之间的 Schema 兼容性,避免因字段变更导致的反序列化异常。Protobuf 更适合 gRPC 服务间调用;JSON 仅用于调试或低吞吐原型阶段。

3.4 数据格式选型决策流程

下图展示了在实时特征生产场景下,如何根据业务需求选择数据格式:

数据格式选型决策流程 实时特征生产场景 吞吐 > 10万 msg/s 或延迟 < 50ms? 是 否 选 Avro 或 Protobuf 可选 JSON(低吞吐场景) Schema 频繁演进? 是 Avro + Schema Registry 否 Protobuf(gRPC 场景)

3.5 数据格式与消息队列的集成示例

以下是一个使用 Kafka + Avro + Schema Registry 的生产者/消费者代码片段,展示实时特征数据的序列化与反序列化:

// 生产者:使用 Avro 序列化特征数据
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");
props.put("schema.registry.url", "http://localhost:8081");

KafkaProducer<String, FeatureRecord> producer = new KafkaProducer<>(props);

FeatureRecord feature = FeatureRecord.newBuilder()
    .setUserId("user_12345")
    .setFeatureName("click_rate_7d")
    .setFeatureValue(0.8765)
    .setTimestamp(System.currentTimeMillis())
    .build();

ProducerRecord<String, FeatureRecord> record =
    new ProducerRecord<>("feature-input", feature.getUserId(), feature);
producer.send(record);
producer.close();
// 消费者:使用 Avro 反序列化特征数据
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "feature-consumer-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "io.confluent.kafka.serializers.KafkaAvroDeserializer");
props.put("schema.registry.url", "http://localhost:8081");
props.put("specific.avro.reader", "true");

KafkaConsumer<String, FeatureRecord> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("feature-input"));

while (true) {
    ConsumerRecords<String, FeatureRecord> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, FeatureRecord> record : records) {
        FeatureRecord feature = record.value();
        System.out.printf("User: %s, Feature: %s, Value: %f%n",
            feature.getUserId(), feature.getFeatureName(), feature.getFeatureValue());
    }
}
关键点: 使用 Avro 时,Schema Registry 的 URL 必须对所有生产者和消费者可见。Schema 兼容性建议设置为 BACKWARD 或 FULL,确保新增字段不会破坏已有消费者。

3.6 本章小结

  • 消息队列选型: Kafka 适合自建高吞吐场景,Kinesis 适合 AWS 全托管,Pulsar 适合多租户与跨地域复制。
  • 数据格式选型: 实时特征生产优先选择 Avro + Schema Registry,兼顾性能与 Schema 演进;Protobuf 适合 gRPC 服务;JSON 仅用于调试。
  • 集成要点: 生产者与消费者必须使用相同的 Schema Registry,并配置兼容性策略,避免运行时反序列化失败。