3、数据采集层:日志采集规范、消息队列选型与数据埋点最佳实践

数据采集层是整个实时特征计算的起点。说白了,你后面所有的模型、规则、实时决策,都依赖这一层把原始数据干净、及时地送进来。我见过太多项目,算法模型再牛,结果数据采集阶段就漏了、乱了、延迟了,最后整个系统都是白搭。

这一章,我们聚焦三个核心问题:日志怎么采、消息队列怎么选、埋点怎么设计。嗯,都是实战中踩过坑才总结出来的东西。

3.1 日志采集规范:别让数据在源头就脏了

日志采集看似简单,但我在项目中遇到过最头疼的问题,往往就出在日志格式不统一。不同团队、不同服务,日志字段名不一样、分隔符不一样、时间格式不一样,到了消费端解析起来简直是一场噩梦。

核心原则:日志采集必须做到「三统一」——统一格式、统一字段、统一时间戳。

3.1.1 推荐日志格式:JSON 行协议

我个人习惯使用 JSON 行协议(JSON Lines)。每行一个完整的 JSON 对象,换行分隔。为什么?因为 JSON 自描述、易扩展、解析效率高,而且几乎所有语言都有原生支持。

// 标准日志格式示例
{"timestamp": "2025-03-15T10:30:00.123Z", "level": "INFO", "service": "user-service", "trace_id": "abc123", "user_id": "u_98765", "event": "login", "duration_ms": 45, "extra": {"ip": "192.168.1.1", "device": "mobile"}}
{"timestamp": "2025-03-15T10:30:01.456Z", "level": "WARN", "service": "order-service", "trace_id": "def456", "order_id": "o_54321", "event": "payment_timeout", "duration_ms": 3002, "extra": {"amount": 199.00}}

小技巧:时间戳统一使用 ISO 8601 格式,带毫秒和时区。千万别用 Unix 时间戳,不同服务精度不一致(秒级 vs 毫秒级),后面做窗口聚合时你会哭的。

3.1.2 必选字段清单

不管业务怎么变,以下字段必须出现在每条日志中:

字段名 类型 说明
timestamp string 事件发生时间,ISO 8601 格式
level string 日志级别:INFO / WARN / ERROR
service string 产生日志的服务名
trace_id string 全链路追踪 ID,用于关联上下游
event string 事件名称,如 login、order_create
duration_ms int 事件耗时(毫秒),用于性能监控
extra object 扩展字段,按业务需求灵活添加

避坑指南:我曾经在一个项目中,日志里混入了换行符,导致 JSON 行协议解析错乱。后来强制要求所有字符串字段做转义,并在采集端做校验。记住:日志采集层一定要有「格式校验」和「脏数据隔离」机制,坏数据不能阻塞好数据的处理。

3.2 消息队列选型:Kafka vs Pulsar

日志采集上来之后,需要一个高吞吐、低延迟的消息队列来缓冲和分发。目前主流的选择就是 Kafka 和 Pulsar。你想想看,这两个到底怎么选?

我两个都用过,说说我的真实感受。

3.2.1 Kafka:成熟稳定,生态丰富

Kafka 是目前最成熟的消息队列,社区活跃,周边生态(Kafka Connect、KSQL、Schema Registry)非常完善。如果你的团队对 Kafka 比较熟悉,而且业务场景以「高吞吐日志采集」和「流式处理」为主,Kafka 基本不会出错。

  • 优势:吞吐量极高(百万级/秒)、持久化可靠、分区机制成熟
  • 劣势:分区数量有限制(建议不超过 1000 个)、Rebalance 时会有短暂不可用、存储与计算耦合
  • 适用场景:日志采集、监控指标、事件溯源、流式 ETL

3.2.2 Pulsar:云原生,分层架构

Pulsar 是后起之秀,它的核心优势是「存储与计算分离」。说白了,Broker 只管计算,数据存在 BookKeeper 里。这意味着你可以独立扩缩容,而且分区数可以轻松到几万个。

  • 优势:分区数无上限、Rebalance 无感、支持多租户、延迟更低
  • 劣势:社区相对较小、运维复杂度略高、部分工具链不如 Kafka 成熟
  • 适用场景:多租户场景、需要大量分区的实时特征计算、跨地域复制

3.2.3 选型对比表

对比维度 Kafka Pulsar
架构 存储与计算耦合 存储与计算分离
最大分区数 建议 ≤ 1000 可到数万
Rebalance 影响 有短暂不可用 无感
消息保留 基于日志段过期 基于 BookKeeper 分段
多租户 需额外设计 原生支持
延迟(P99) 5-10ms 2-5ms
社区成熟度 极高 中等

我的建议:如果团队规模不大、运维能力有限,选 Kafka 更稳妥。如果你们在做云原生架构、需要支持大量 Topic 和分区,或者有多租户需求,Pulsar 值得一试。我个人在实时特征计算项目中,更倾向于 Pulsar,因为它的分层架构让存储和计算可以独立扩展,非常适合特征数据量大、消费端多的场景。

3.3 数据埋点最佳实践

埋点,就是决定「采什么数据」和「怎么采」。很多团队在埋点上走了弯路——要么埋得太少,特征不够用;要么埋得太多,数据量爆炸,成本飙升。

3.3.1 埋点设计原则

我总结了一个「三层埋点法」:

  1. 基础层:所有服务必须埋的通用事件,如请求入口、响应出口、异常、超时。这些用于监控和链路追踪。
  2. 业务层:核心业务事件,如登录、下单、支付、退款。这些用于实时特征计算和业务分析。
  3. 行为层:用户行为事件,如点击、滑动、停留时长。这些用于用户画像和推荐系统。

关键原则:埋点数据必须「可回溯、可校验、可治理」。每条埋点都要有明确的 owner 和用途,避免「埋了不用」的垃圾数据。

3.3.2 埋点 SDK 设计要点

埋点 SDK 是数据采集的「最后一公里」。我见过最糟糕的做法是每个业务方自己写日志,格式五花八门。正确的做法是统一 SDK,强制规范。

// 埋点 SDK 调用示例(伪代码)
// 统一埋点接口
tracer.emit("order_create", {
  user_id: "u_98765",
  order_id: "o_54321",
  amount: 199.00,
  payment_method: "wechat",
  // SDK 自动补充:timestamp, trace_id, service, host, env
});

// SDK 内部自动完成:
// 1. 补充公共字段
// 2. 序列化为 JSON
// 3. 异步发送到本地 Agent 或直接写入 Kafka
// 4. 失败重试 + 本地缓存兜底

避坑指南:我曾经遇到一个线上事故——埋点 SDK 同步发送阻塞了业务线程,导致接口响应时间从 10ms 飙升到 500ms。后来强制要求 SDK 必须异步发送,且使用独立线程池。另外,SDK 一定要有「熔断机制」,当消息队列不可用时,不能拖垮业务服务。

3.3.3 埋点数据治理

埋点数据量一大,治理就成了大问题。我推荐建立「埋点元数据中心」,统一管理所有埋点的定义、字段、owner、变更历史。

元数据字段 说明
event_name 事件名称,全局唯一
owner 负责团队/个人
fields 字段列表及类型
sample_rate 采样率,用于控制数据量
status 启用/废弃/灰度
created_at 创建时间

小技巧:对于非核心的埋点,可以设置采样率。比如用户行为日志,采样 10% 就足够做特征分析了。但注意:涉及金额、支付等核心事件,必须全量采集。

3.4 本章知识体系总览

下面这张图,把数据采集层的核心逻辑串起来了。从日志产生到消息队列,再到下游消费,每一步都有规范和选型要点。

数据采集层知识体系总览 数据采集层知识体系总览 应用服务日志 用户行为埋点 外部系统事件 日志采集规范:JSON 行协议 · 三统一 · 必选字段 统一格式 · 统一字段 · 统一时间戳 Kafka 成熟稳定 · 生态丰富 Pulsar 云原生 · 存储计算分离 数据埋点最佳实践:三层埋点法 · SDK 设计 · 元数据治理 基础层 · 业务层 · 行为层 | 异步发送 · 熔断机制 · 采样控制 实时特征计算引擎(下游消费) 选型依据:吞吐量、延迟、运维

数据采集层是实时特征计算的「地基」。地基不稳,上层建筑再漂亮也是空中楼阁。日志规范、消息队列选型、埋点设计,这三件事做好了,后面的实时计算才能顺畅跑起来。