消息队列 Kafka 实践:千里达票务系统行为日志与埋点实时流
之前千里达票务系统的用户行为埋点是直接打 SLS,早期流量小没问题,日活上来之后埋点量到每秒几万条,SLS 检索成本飙升,而且没法做实时计算(比如"当前景区实时在园人数")。引入阿里云消息队列 Kafka 作为埋点总线:小程序/APP 埋点 → Kafka → 一边落 SLS 归档,一边算实时指标写 Redis。
一、为什么要 Kafka
- 高吞吐:埋点是写多读少,Kafka 单集群轻松扛日亿级;
- 多消费方:同一份埋点,实时算一套、离线归档一套、风控用一套;
- 解耦:埋点 SDK 只管发,下游怎么用不关心。
二、拓扑
小程序/APP 埋点 SDK
→ 阿里云 API 网关
→ Kafka Topic: user_event
├─ Flink 实时计算 → Redis (实时在园人数)
└─ Consumer → SLS/MaxCompute (离线分析)
三、关键实现
3.1 生产端(小程序埋点)
// 埋点统一上报
report("scene_view", {
scene_id: "SC1001",
source: "h5",
ts: Date.now()
});
网关把消息统一写到 Kafka user_event Topic,按 scene_id 做 key,保证同景区事件有序。
3.2 实时消费(算在园人数)
// Flink 任务
env.addSource(new KafkaSource<>("user_event"))
.filter(e -> e.eventType == "enter_gate" || e.eventType == "exit_gate")
.keyBy(e -> e.sceneId)
.process(new InParkCounter()) // 进闸+1, 出闸-1
.addSink(new RedisSink());
3.3 保留策略
- 实时 Topic 保留 3 天(够窗口计算);
- 归档 Topic 保留 30 天(审计/回查);
- 超长期数据自动落 MaxCompute。
四、踩坑复盘
- 消息乱序:同一用户进出闸事件乱序,在园人数算成负数。解决:事件带时间戳,窗口按 event_time 而非 processing_time;
- 消费积压:一次发版埋点写错格式,下游崩了,Kafka 堆了 500 万条。解决:消费端加容错,坏消息进死信队列;
- Topic 规划混乱:早期所有埋点塞一个 Topic。解决:按业务域拆 topic,event_type 做 tag;
- 磁盘打满:保留策略没配,Kafka 磁盘一周用满。解决:按业务重要性分级保留期。
五、效果与总结
Kafka 接进来后,实时在园人数从"5 分钟级"变成"秒级",DataV 大屏数据真的"活"了。对千里达票务系统这种多端、多事件、要实时算的业务,Kafka 是连接业务和数据平台的总线。