日志采集与传输:高性能日志采集工具、传输协议与序列化格式

做高频交易系统,日志这东西,很多人觉得不就是记个时间戳、打个字符串嘛?

我刚开始也这么想。直到有一次,系统在盘中突然卡顿,我翻日志发现——采集端把CPU吃掉了30%。嗯,从那以后,我再也不敢小看日志采集这环了。

今天咱们聊聊日志采集与传输的三个核心问题:用什么工具收日志、用什么协议传日志、用什么格式存日志。说白了,就是让日志又快又稳地从你的交易节点,流到分析中心。

核心要点:高频场景下,日志采集不能成为性能瓶颈。采集工具要轻量、传输协议要低延迟、序列化格式要紧凑。

高性能日志采集工具

市面上主流的采集工具有两个:Filebeat 和 Fluentd。我个人习惯用 Filebeat 做轻量采集,Fluentd 做复杂路由。为什么?

Filebeat:轻量级选手

Filebeat 是 Elastic 家的东西。它有多轻?我记得在某个期货公司项目里,一台 8 核机器上跑 Filebeat,CPU 占用不到 5%。

  • 资源占用极低:Go 语言编写,内存常驻 20MB 左右
  • 背压机制:当下游处理不过来时,Filebeat 会自动降速,不会丢数据
  • 多行合并:高频交易日志经常跨多行,Filebeat 的 multiline 配置能搞定
# filebeat.yml 配置示例
filebeat.inputs:
- type: log
  paths:
    - /var/log/hft/trade*.log
  multiline:
    pattern: '^\d{4}-\d{2}-\d{2}'
    negate: true
    match: after

output.kafka:
  hosts: ["kafka1:9092", "kafka2:9092"]
  topic: "hft-logs"
  compression: gzip

我的经验:Filebeat 的 harvester_buffer_size 默认是 16KB。如果你日志行很长(比如包含完整订单快照),建议调到 64KB,否则会有截断风险。

Fluentd:路由大师

Fluentd 是 Ruby 写的,资源占用比 Filebeat 高一些。但它强在插件生态和路由能力。

我曾经遇到一个场景:交易日志需要同时发往 Kafka 做实时分析、发往 S3 做归档、还要过滤掉心跳日志。用 Fluentd 的 tagfilter 机制,几行配置就搞定了。

# fluentd.conf 配置示例
<source>
  @type tail
  path /var/log/hft/order*.log
  tag hft.order
  format json
</source>

<filter hft.order>
  @type grep
  <exclude>
    key level
    pattern ^heartbeat$
  </exclude>
</filter>

<match hft.order>
  @type copy
  <store>
    @type kafka2
    brokers kafka1:9092
    topic hft-orders
  </store>
  <store>
    @type s3
    s3_bucket hft-logs-archive
    path logs/
  </store>
</match>

避坑指南:我曾经在生产环境用 Fluentd 的 in_tail 插件,发现日志文件轮转时偶尔会漏掉最后几行。后来加了 pos_file 持久化记录读取位置,才彻底解决。记住,pos_file 一定要配!

日志传输协议

日志从采集端到服务端,走什么协议?我见过三种主流方案:TCP、UDP、Kafka。你想想看,高频交易场景下,延迟和可靠性哪个更重要?

其实两者都重要。但不同场景侧重点不同。

协议 延迟 可靠性 适用场景
TCP 中等(有重传机制) 订单日志、风控日志
UDP 低(无确认) 低(可能丢包) 行情快照、监控指标
Kafka 低(批量+异步) 高(持久化+副本) 全量日志、流式处理

TCP:稳,但慢

TCP 有三次握手、有重传、有拥塞控制。说白了,它为了可靠牺牲了速度。我建议订单日志走 TCP,因为丢一条订单日志,复盘时可能找不到原因。

UDP:快,但会丢

UDP 无连接、无确认。行情快照用 UDP 很合适——丢了就丢了,下一帧马上来。我曾经在行情网关里用 UDP 广播日志,延迟从 2ms 降到了 0.3ms。

Kafka:兼顾速度与可靠

Kafka 是高频交易日志传输的标配。为什么?因为它把日志持久化到磁盘,还能多副本。就算消费者挂了,重启后还能从断点继续消费。

我个人习惯:所有交易日志先写到 Kafka,再由下游消费者决定怎么处理。这样采集端和消费端解耦,系统更健壮。

# Kafka 生产者配置(Java 示例)
Properties props = new Properties();
props.put("bootstrap.servers", "kafka1:9092");
props.put("acks", "all");          // 等待所有副本确认
props.put("retries", 3);           // 重试次数
props.put("batch.size", 16384);    // 16KB 批量发送
props.put("linger.ms", 5);         // 最多等待 5ms
props.put("compression.type", "snappy");

我的建议:Kafka 的 acks=all 虽然慢一点,但能保证不丢数据。高频交易里,数据完整性比那几毫秒延迟更重要。

序列化格式

日志内容用什么格式存?JSON 还是 Protobuf?

这个问题我纠结过很久。JSON 可读性好,但体积大、解析慢。Protobuf 体积小、解析快,但调试麻烦。

JSON:简单直观

JSON 是文本格式,人类可读。开发阶段调试很方便。但它的体积比二进制格式大 3-5 倍。在高频场景下,网络带宽和解析时间都是成本。

// JSON 格式日志示例
{
  "timestamp": "2024-01-15T10:30:00.123456Z",
  "symbol": "BTC-USDT",
  "side": "BUY",
  "price": 42000.50,
  "quantity": 0.1,
  "order_id": "ORD-20240115-001"
}

Protobuf:紧凑高效

Protobuf 是二进制格式,体积小、解析快。我做过测试:同样的日志内容,Protobuf 比 JSON 小 60%,解析速度快 4 倍。

// Protobuf 定义文件
syntax = "proto3";
message OrderLog {
  int64 timestamp = 1;      // 纳秒时间戳
  string symbol = 2;
  enum Side { BUY = 0; SELL = 1; }
  Side side = 3;
  double price = 4;
  double quantity = 5;
  string order_id = 6;
}

我的选择:生产环境用 Protobuf,开发环境用 JSON。或者——在采集端用 Protobuf 压缩传输,到分析端再转成 JSON 方便查看。两全其美。

知识体系总览

下面这张图,是我对日志采集与传输的整体理解。你可以把它当作本章的思维导图。

日志采集与传输 采集工具 传输协议 序列化格式 Filebeat Fluentd TCP UDP Kafka JSON Protobuf 核心原则:轻量采集 + 可靠传输 + 紧凑序列化 Filebeat + Kafka + Protobuf 是高频交易日志的黄金组合 实战建议 1. 订单日志用 Filebeat + Kafka + Protobuf 2. 行情快照用 UDP 传输,丢包可接受

总结一下:日志采集与传输,说白了就是三个选择——工具选 Filebeat 还是 Fluentd、协议选 TCP/UDP 还是 Kafka、格式选 JSON 还是 Protobuf。没有银弹,只有最适合你场景的方案。

我个人推荐:Filebeat + Kafka + Protobuf 作为高频交易日志的黄金组合。轻量、可靠、高效。你可以在自己的测试环境里跑一下,看看延迟和吞吐量的变化。


专注资料整理