指标监控
一、架构组成
SDK -> TencentCloud_CloudMonitor_Agent -> 云监控接口机 -> kafka -> 指标计算(flink) -> Kafka -> writer 存储/告警
二、工作经历
数据量:云基础监控上报指标量8.3万亿/d,存储记录数8500亿/d
自定义监控上报记录数2200亿/d,存储记录数600亿/d
协议设计:窄表改造成宽表,业界著名监控系统prometheus、statsd等协议采用的是窄表(一张表一个指标)上报数据,通过在Tccm-SDK和Tccm-Agent支持窄表转换宽表(一张表多个指标),大幅降低了数据的上报流量。
性能:例如开源Flink自监控数据上报流量从10GB/m降至2GB/m
Tccm-Agent的设计与开发:调研分析了业界和公司内监控Agent的实现,最终采用微内核架构,面向功能进行拆分插件种类,分为输入、预处理、聚合、输出插件,以配置文件的方式实现插件注册机制进行插件管理,不同种类的插件之间通过线程安全队列进行连接通信,插件完全解藕,可扩展性强。且快速适配了prometheus、statsd、influx line protocol等开源协议上报数据的用户,可以方便的将不同的数据协议进行整理、统一格式且将数据聚合之后上报到云监控中台。
性能:Tccm-Agent可接收流量80M/s,占用资源CPU:2C,MEM:60M
Tccm-SDK的设计与开发:分析了开源界(prometheus、datadog、influxdb等)和公司内(007、monitor等)监控系统的SDK的上报协议和实现,采用宽表(一张表多个维度多个指标)作为数据流转或存储协议,以文本作为数据传输协议,实现线程安全的多写单读队列作为中介进行缓冲数据,定时读取数据进行聚合处理后上报到Tccm-Agent;功能上支持公司内部和开源界所有的数据统计方式;性能上每秒上报指标量可以达到百万级别,而其他SDK都只是在十万级别;并且支持多语言(go、c++、java等)
性能:SDK在不丢数据的情况下,最高可传输100w指标数每秒,流量70M/s,占用资源CPU:1.1C,MEM:100M
三、pull or push
1. pull
依赖服务发现系统帮助我们发现 pod 。prometheus 采用这种方式。
核心组成 | 说明 |
---|---|
服务发现系统 | 1.主机的服务发现(一般依赖公司内部CMDB系统) 2.应用服务发现(Consul) 3.PaaS服务发现(Kubernetes) |
Pull核心模块 | 1.通用的协议 2.配置项(拉取间隔、超时间隔、指标过滤、指标合并等) |
应用侧SDK | 1. 暴露端口 |
Exporter | 1.拉取系统指标 2.提供标准Pull接口 |
pull 的分布式解决方案:

- pull 模块与监控后端解耦,pull 模块单独部署
- pull 模块需要分布式协同,例如,从服务发现模块获取被监控的机器列表,对这些机器进行 hash 后取模来决定由那个 pull agent 来负责哪些节点
- 新增一个配置中心(可选)来管理各个 pull Agent
缺点:
- 还是存在单点瓶颈,所有 Agent 都需要请求服务发现模块
- Agent 扩容之后,监控目标变化,容易产生数据重复或者缺失
2. push
influxDB 的 TICK( telegraf / influxDB / chronograf / kapacitor )
核心组成 | 说明 |
---|---|
Push Agent | 1.拉取各类被监控对象的指标数据并推送服务端 2.可以和被监控系统耦合部署或单独部署 |
Config Center (可选) | 1.提供中心化动态配置能力(例如:监控目标、采集间隔、指标过滤、指标处理、远端目标) |
应用侧SDK | 1.支持发送数据直接到监控后端 2.也可发送到本地Agent |
3. 对比
一级分类 | 二级分类 | pull | push |
---|---|---|---|
原理与部署 | 配置 | 原生中心化配置 | 端上配置,通过配置中心支持中心化 |
原理与部署 | 监控对象分类 | 依赖服务发现机制,例如 zookeeper、etcd、consul 等注册中心 | 由应用、agent自主上报,无需服务发现模块 |
原理与部署 | 部署方式 | 1. 应用程序暴露端口,接入服务发现,支持 pull 协议 2. 其他系统如MySQL、nginx 等中间件依赖适配器(Exporter)抓取指标再提供 pull 端口 3. 部署方式太过复杂,维护代价较高 |
1. Agent 可以作为统一代理,抓取主机、MySQL 等中间件数据推送到监控系统;Agent 也可以作为转发器接收应用推送 2. 应用主动推送到监控系统 3. 部署方式较为方便 |
可扩展性 | 可扩展性 | 1. 依赖客户端实现扩展 2. 有服务发现的单点瓶颈问题 3. 扩容后,数据容易出现重复或者缺失 |
简单,Agent 本身可横向扩展 |
能力对比 | 监控对象存活性 | 简单 | 无法区分对象未存活的原因 |
能力对比 | 短生命周期(Job、Serverless) | 难以适用 | 适用 |
能力对比 | 指标获取灵活性 | 可以按需获取 | 被动接受,需要配置过滤器额外支持 |
能力对比 | 应用耦合性 | 应用与监控系统解耦,应用无需关心后端地址 | 耦合性相比 pull 较高,比如可能需要处理 Push 错误如何处理等 |
机器/人力代价 | 资源消耗 | 1. 应用暴露端口方式资源消耗低 2. Exporter 方式资源消耗较高 |
1. 应用推送方式资源消耗低 2. Agent 方式资源消耗较低(可同时采集多套系统) |
机器/人力代价 | 安全性保证 | 需要保证应用暴露端口的安全性以及 Exporter 端口的安全性,容易被 DDos 攻击或者出现数据泄露 | 较为安全,Agent 与服务端可以进行带有加密、鉴权的数据传输 |
机器/人力代价 | 核心运维消耗 | 1. Pull Agent稳定性与扩容 2. 服务端稳定性与扩容 3. 服务发现系统稳定性 4. Exporter 稳定性与扩容 5. 网络连通性保障(反向连通性、跨集群) |
1. Push Agent 稳定性 2. 服务端稳定性与扩容 3. 配置中心稳定性与扩容(可选) 4. 网络连通性保障(正向连通性) |
四、宽表和窄表

旧的存储方式使用窄表,一张表只有一个指标,维度冗余特别严重,写入量大,存储成本高。但是也有点查速度快、结构统一、读写具有幂等性 等优点。我经过调研,很多场景业务在同一维度下会有多个指标的监控,因此采用宽表作为存储方式,是我们的写入和存储成本大幅降低,并且减小了 Rollup 压力;指标计算模块 Flink 自监控数据上报流量从10GB/m降至2GB/m
五、SDK 设计与对比
SDK | 功能 | 性能 |
---|---|---|
prometheus SDK | 1. 统计方式支持不足,例如:set、first、last 2. counter 统计方式为进程启动后一直递增,重启会出现负值,引发误告警 3. 数据以窄表传输,占用存储空间大。且窄表和宽表之间的转换配置较困难 |
1. 会在内存中存储所有指标,占用内存非常大 2. 指标比较多的情况下,每次传输数据量特别大,超过阈值会被丢掉 |
Statsd SDK | 1. 使用文本传输数据(使用特殊符号分割),造成业务数据对特殊符号敏感 2. 数据以窄表传输,占用存储空间大。且窄表和宽表之间的转换配置较困难 3. 官方各语言 SDK 不齐全,生态不完善 |
数据未做聚合,冗余数据量特别大,占用带宽资源多 |
influxdb SDK | 1. 数据没有统计方式,k-v 形式数据,存储的是原始数据,占用存储空间大 2. 使用文本传输数据,对敏感字符过敏 |
数据无法做聚合,冗余数据量大,占用带宽资源多 |
Tccm SDK | 1. 统计方式齐全,支持 sum、max、min、first、last、counter、gauge、histogram、summary、set 等 2. 数据以宽表形式组织,存储空间低 3. 数据聚合,相同维度的指标经过聚合后数据量特别小,传输数据少,带宽占用小 4. 使用文本进行传输,对敏感字符进行过滤,且没有数据序列化,CPU 占用少 |
上报数据量每秒可以达到百万级别 |
Tccm SDK 采用宽表(一张表多个维度多个指标)作为数据流转或存储协议,以文本作为数据传输协议,实现线程安全的多写单读队列作为中介进行缓冲数据,定时读取数据进行聚合处理后上报到Tccm-Agent;功能上支持公司内部和开源界所有的数据统计方式。线程安全队列以 内存池 规避需要申请内存块的耗时,以链表链接这些内存块,读和写线程的临界区只是链表的头指针;写线程之间的临时区只是链表的尾指针;锁粒度特别低,而且锁范围内只有简单的赋值和++操作,不会发生异常情况。在有锁和无锁中, 我选择有锁,因为无锁的实现并不能给我带来性能上大的提升;而且代码实现起来较为简单,收益和付出成正比。 后面读线程拿到数据进行聚合后可选 tcp/udp 上报。
【SDK 的内存池,业务调用接口在初始化一个指标的时候,会从 SDK 的内存池拿到一块内存来使用(拿内存块的过程要加锁)。线程安全队列是有长度的,20w 的长度,每个内存块大小 512 字节,因此最大的情况会占用 97M 的空间。内存池不会一下子申请 20w 个内存块,而是先初始化时申请 5w 个,然后后续发现内存池里面拿不到内存块,则进行申请内存,到达 20w 封顶。读线程读完之后还给内存池。】
六、Agent 的设计
调研分析了业界和公司内监控Agent的实现,最终采用微内核架构,面向功能进行拆分插件种类,分为输入、预处理、聚合、输出插件,以配置文件的方式实现插件注册机制进行插件管理,不同种类的插件之间通过线程安全队列进行连接通信,插件完全解藕,可扩展性强。且快速适配了prometheus、statsd、influx line protocol等开源协议上报数据的用户,可以方便的将不同的数据协议进行整理、统一格式且将数据聚合之后上报到云监控中台。
采用微内核架构,可扩展性特别高,确认了“变”和“不变”的边界。即内核是永远不变的,而提供 interface 接口 API 用来实现插件。通过公司内部平台七彩石进行配置管理。较为灵活。使用 go 语言开发,充分利用了 go 语言协程、支持高并发的特性。而且采用 push 的方式可以简单高效的支持分布式,方便扩容。
Agent 的缺点:
如何保证数据不会丢失?agent 一般会保存 10 秒的数据做聚合,聚合后发送,那这 10 秒内 agent 挂了,数据丢失如何处理?agent 的定位是在客户端,相当于客户端的一个组件,和业务服务同机部署。我们面向的场景是对于数据的准确度和性能上,优先选择性能的场景,不会处理对数据准确度要求特别高的场景,比如付费数据、银行数据之类。对数据准确度较高的场景,agent 数据应该直接落磁盘。我们会力保数据的准确性做到 3 个 9 以上。而且我们监控的目的是帮助业务快速发现问题,而不是传输业务数据,对数据做业务相关的用途。也就是用户的数据是允许丢的,但丢了之后一定要告警出来。基于这个大方向,我会将数据放在内存中处理,同时为了弥补这方面的缺失,在 agent 上会有进程监控和自动监测重启功能,可以及时告警。做架构需要取舍,不能既要性能又要数据安全,只能选择其中,尽最大可能保证另外一个。
还有一点,Agent 存在的主要意义是做数据预处理、预聚合,减少数据量,保护接入层。如果像 kafka 一样,选择数据存储磁盘,就丧失了Agent 存在的意义;而且虽然保证了数据的安全,但也是以性能为代价,不符合我们的场景。