第四章:APM系统的整体架构、数据采集层、存储层、展示层设计
好,咱们直接切入正题。APM系统怎么搭?很多人一上来就选开源组件,结果拼起来发现根本跑不动。我见过太多这样的案例了。
我个人习惯,先画架构图。不是那种花里胡哨的PPT图,而是能落地的技术架构。说白了,APM就三层:采集层、存储层、展示层。每层都有坑,咱们一个一个说。
4.1 整体架构:三层模型
APM系统的核心,其实就是个数据管道。数据从业务系统里捞出来,经过处理,最后变成你能看懂的图表。我把它拆成三层:
- 数据采集层:负责从应用、中间件、基础设施里捞数据。包括Trace、Metric、Log。
- 数据存储层:负责存这些海量数据。嗯,这里要注意,不同类型的数据得用不同的存储引擎。
- 数据展示层:负责把数据变成可视化的东西。告警、大盘、链路追踪,都在这层。
你想想看,这三层如果耦合在一起,后期扩容就是噩梦。我建议每层之间用消息队列解耦,比如Kafka。这样采集层挂了,存储层还能继续干活。
核心原则:采集层无状态,存储层可扩展,展示层可配置。
4.2 数据采集层:怎么捞数据?
采集层是APM的起点。数据捞不全,后面全是白搭。我在项目中遇到过,业务方说“你们APM不准”,结果一查,是采集器漏了半个机房的数据。
采集层主要干三件事:
- Agent注入:Java应用用ByteBuddy或Java Agent,Go应用用eBPF或SDK埋点。我个人偏好Java Agent,无侵入,业务方不用改代码。
- 数据协议:业界标准是OpenTelemetry。别自己造协议,否则后面对接第三方工具会哭的。我曾经见过一个团队自己搞了一套二进制协议,结果想接Grafana,还得写个转换器。
- 采样策略:全量采集?你存储扛不住的。我建议头部采样+尾部采样结合。比如,错误链路全采,正常链路按1%采样。
避坑指南:我曾经在采集层忘了加限流,结果业务高峰期,Agent把业务线程池打满了。后来我加了信号量限流,每个Agent最多占用5%的CPU。
代码示例?给你一个简单的Java Agent采集器骨架:
// 基于ByteBuddy的Agent注入示例
public class MyAgent {
public static void premain(String args, Instrumentation inst) {
new AgentBuilder.Default()
.type(ElementMatchers.nameStartsWith("com.mycompany"))
.transform((builder, type, classLoader, module) ->
builder.method(ElementMatchers.any())
.intercept(MethodDelegation.to(TraceInterceptor.class))
).installOn(inst);
}
}
4.3 数据存储层:海量数据往哪放?
存储层是APM的瓶颈。为什么?因为数据量大、写入快、查询模式复杂。我见过有人把所有数据塞进Elasticsearch,结果查询慢得像蜗牛。
我的建议是:分而治之。不同类型的数据,用不同的存储引擎:
| 数据类型 | 存储引擎 | 原因 |
|---|---|---|
| Trace(调用链) | Elasticsearch 或 ClickHouse | 需要全文检索和聚合分析 |
| Metric(指标) | Prometheus + Thanos 或 VictoriaMetrics | 时序数据,高压缩率,快速聚合 |
| Log(日志) | Elasticsearch 或 Loki | 日志需要全文搜索,Loki更省资源 |
嗯,这里要注意。Trace和Log可以共用ES集群,但一定要分索引。我建议按天分索引,比如traces-2025-01-01。这样查询时只扫一个索引,速度快很多。
警告:别把Metric存进ES!时序数据用Prometheus,查询性能差10倍以上。我踩过这个坑,后来迁移到VictoriaMetrics,查询从30秒降到了0.5秒。
存储层的另一个关键点是数据生命周期管理。热数据存SSD,冷数据存HDD或对象存储。我一般这样配:
- 最近7天:SSD,全量数据
- 7-30天:HDD,降采样数据(比如1分钟聚合为5分钟)
- 30天以上:对象存储(S3/MinIO),只保留Trace的Span摘要
4.4 数据展示层:让数据说话
展示层是用户直接看到的。做得不好,再牛的数据也没用。我见过一个APM系统,数据全对,但UI太丑,业务方直接不用了。
展示层我分三个模块:
- 链路追踪:展示调用链的拓扑图。我用Grafana + Tempo,或者自研的拓扑引擎。关键是要能展示耗时分布和错误链路。
- 指标大盘:展示QPS、延迟、错误率。Grafana是标配,配上Prometheus数据源,直接拉满。
- 告警中心:基于规则触发告警。我习惯用Alertmanager,配合飞书或钉钉机器人。
你想想看,展示层最怕什么?数据延迟。如果采集层到展示层延迟超过1分钟,业务方就会觉得“这系统不准”。我建议展示层直接读实时数据,不要经过ETL。
个人经验:展示层一定要支持“下钻”。比如,看到某个接口延迟高,点一下就能看到具体的Trace。否则用户只能看到问题,找不到根因。
展示层的技术选型,我推荐这样:
| 功能 | 推荐工具 | 备注 |
|---|---|---|
| 链路追踪UI | Grafana Tempo 或 Jaeger UI | Grafana生态更统一 |
| 指标大盘 | Grafana | 插件丰富,支持多数据源 |
| 告警管理 | Alertmanager + 自定义Webhook | 支持静默、分组、抑制 |
4.5 架构设计的避坑指南
最后,我总结几个常见的坑。这些都是我亲身经历过的:
- 坑一:采集层和存储层直连。 我曾经这么干过,结果采集器一挂,存储层直接被打满。后来加了Kafka做缓冲,稳如老狗。
- 坑二:存储层不分冷热。 所有数据都放SSD,成本高得离谱。后来我加了冷热分离,成本降了60%。
- 坑三:展示层不做权限控制。 业务方能看到别人的链路,这涉及数据安全。我建议用Grafana的RBAC,或者自建权限网关。
嗯,架构设计这块,说白了就是平衡。平衡性能、成本、可维护性。没有完美的架构,只有最适合你的架构。我建议你从最小可行产品开始,先跑通一条链路,再逐步扩展。
下一章,咱们聊聊数据采集层的具体实现,包括Agent的部署和配置。到时候我会分享一个我在生产环境踩过的坑,保证让你少走弯路。