日志传输:消息队列选型与路由策略
日志从各个业务系统产生,到最终落盘存储,中间这一步非常关键——数据传输。你想想看,成百上千台机器,每秒可能产生几十万条日志。直接写数据库?那不现实。直接写文件?集中存储又没法做。所以,消息队列这个中间层,几乎是必选项。
我个人习惯把消息队列比作一个“缓冲水池”。上游的日志源是河流,下游的存储系统是水库。没有水池调节,雨季直接冲垮水库,旱季河流又干涸。消息队列干的就是这个事——削峰填谷、解耦、缓冲。
选消息队列,核心看三点:吞吐量、持久化能力、路由灵活性。
为什么是消息队列?三条理由
很多刚接触日志中心的同学会问:我直接用 Logstash 或者其他 agent 推到后端不行吗?嗯,小规模可以,但企业级场景下有几个硬伤:
- 背压问题:后端存储一旦抖动,上游日志采集直接阻塞,业务线程可能跟着遭殃。我在项目里遇到过,ES 集群做滚动重启,结果日志采集线程全部 hang 住,业务接口调用链也跟着超时,排查了好久才发现是 Logstash 的 output 阻塞了 input。
- 数据丢失风险:网络抖动、进程崩溃,内存里的日志说没就没。没有持久化机制,你根本不敢关采集进程。
- 无法跨系统订阅:日志不仅要给存储,还要给实时告警、行为分析、审计报表。多个下游消费者,总不能每个都去拉日志源吧?
消息队列正好解决这三个问题。它能扛住流量洪峰,能持久化保障不丢数据,还能让多个消费者各自独立地消费。
三大主流队列,怎么选?
目前企业级审计日志场景,主要看这三个:Kafka、RabbitMQ、Pulsar。各有各的脾气,我一个个说。
| 特性 | Kafka | RabbitMQ | Pulsar |
|---|---|---|---|
| 核心模型 | 分区日志(Commit Log) | AMQP 队列 / 交换机 | 分层架构(BookKeeper + Broker) |
| 吞吐量 | 百万级 msg/s ★★★★★ | 万级 msg/s ★★★ | 百万级 msg/s ★★★★★ |
| 持久化 | 磁盘顺序写,强持久化 | 内存为主,可持久化 | 计算与存储分离,BookKeeper 持久化 |
| 路由复杂度 | 基于 Topic + Partition 分区 | 基于 Exchange + Binding Key 灵活路由 | Topic + Subscription 模型 |
| 运维复杂度 | 需要 Zookeeper/KRaft 依赖 | 相对简单,自带管理界面 | 组件较多,依赖 BookKeeper |
| 典型场景 | 海量日志流水、数据管道 | 业务消息、任务分发 | 混合负载、跨地域复制 |
说白了,审计日志这个场景,绝大多数企业最终选的都是 Kafka。为什么?我总结了三条:
- 吞吐量是第一优先级。审计日志不像业务消息那样要求低延迟,它更在乎能不能稳定接收海量数据。Kafka 的批处理和顺序写,天生就是为这个准备的。
- 持久化可靠。Kafka 的日志段文件设计,断电不会丢数据。我见过一些团队用 RabbitMQ 做日志传输,结果消息堆积太多,内存直接 OOM,消息全丢了——这在审计场景下是不可接受的。
- 生态成熟。Logstash、Fluentd、Filebeat,甚至自定义客户端,都有成熟的 Kafka 集成。你不用自己造轮子。
但话说回来,如果你的日志量不大(比如一天几百万条),而且需要非常灵活的路由规则(比如按日志等级、按业务模块分发到不同后端),RabbitMQ 的 Exchange 模型用起来更顺手。Pulsar 我看到一些头部互联网公司在推,它解决了 Kafka 的一些痛点,比如扩容时不用搬迁数据、计算存储分离更灵活,但运维门槛确实高了不少。
曾经有个团队,日志量不大但选择了 Kafka,结果运维配置不当,单分区消费并行度上不去,日志消费延迟越来越高。选型不仅要看吞吐量上限,还要看团队是否有对应的运维能力。
路由策略:日志该去哪儿?
选定了队列,接下来是怎么路由日志。审计日志不像一般业务日志随便扔一个 Topic 就行,它需要精细化分流。我一般按以下原则设计:
- 按业务域分 Topic:交易日志、操作日志、安全日志、系统日志各一个 Topic。这样下游存储可以针对不同类型做不同分区数、保留策略。
- 按时间分 Partition:日志是时序数据,通常用时间作为分区键。举个例子,Kafka 里一天一个日志目录,消息按小时 hash 到不同的 Partition,这样消费端可以按时间窗拉取,还能方便做冷热数据分离。
- 关键字段路由:安全日志需要按用户 ID 或 IP 路由,保证同一个用户的所有操作落在同一个 Partition 里,方便做顺序审计回放。
下面是一个常见的路由拓扑图,我把它画成了一张 SVG:
生产环境中,我建议日志源侧的采集 agent 直接按 Topic 分流,不要在消息队列侧再用路由插件做二次重分发,减少中间环节的性能损耗。
生产配置的几个坑
最后,分享几个我在生产环境里踩过的坑,也是你现在就要注意的地方:
- Partition 数量不是越多越好。Kafka 的 Partition 影响消费并行度和文件句柄数。我曾经在一个审计项目里,分区数设到了 128,结果集群文件句柄数飙升,IO 反而下降了。一般来说,日志场景每 Topic 分区数 = 下游消费者实例数 × 2 或者 3 就够用了。
- 消息大小限制要提前评估。审计日志里偶尔会包含附件或大段 SQL 文本。Kafka 默认的消息大小是 1MB,如果你的日志单条超过这个限制,生产端会报错。需要同步调整 broker 端和 producer 端的 max.message.bytes 参数。
- 消费端要设计幂等性。消息队列保证“至少一次”,不保证“恰好一次”。审计日志不允许重复计费或重复告警,所以消费端要做好去重。我的做法是:每条日志带一个全局唯一的 TraceID,消费时先查 Redis 或本地布隆过滤器判重。
我曾经接手过一个审计系统,日志一直有重复,排查发现是 Kafka consumer 在 rebalance 后重复拉取了未提交 offset 的数据。解决方案很简单:开启 Kafka 的幂等生产者,同时消费端启用事务或手动提交 offset,rebalance 前先确认处理完毕。