Java写的CloudWatch指标导出器,让Prometheus轻松采集AWS监控数据

发布时间:2026/6/9 9:06:11

Java写的CloudWatch指标导出器,让Prometheus轻松采集AWS监控数据 本文还有配套的精品资源点击获取简介一个用Java开发的轻量级工具能把AWS CloudWatch里的各种监控指标比如EC2 CPU使用率、RDS连接数、Lambda调用次数等实时转换成Prometheus能直接抓取的格式。支持Java 8编译用mvn package运行就一条java -jar命令指定端口和YAML配置文件自带example.yml参考默认走9106端口。认证走AWS标准凭证链环境变量AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY、IAM角色、~/.aws/credentials文件都行最小权限只要cloudwatch:ListMetrics和cloudwatch:GetMetricStatistics如果要用标签筛选功能aws_tag_select再加个tag:GetResources就行。配置全靠YAML可以按命名空间、指标名、维度如InstanceId、LoadBalancerName、统计方式Average/Sum/Maximum、采样周期比如5分钟和抓取间隔灵活设置。带Dockerfilebuild完就能docker run跑起来项目结构规范含完整Maven配置pom.xml、源码src/main、测试src/test、许可证LICENSE、说明文档README.md、贡献指南CONTRIBUTING.md和安全策略SECURITY.md适合嵌入现有Prometheus监控体系也方便二次开发或定制指标采集逻辑。1. 项目概述为什么我们需要一个“CloudWatch导出器”在 AWS 上跑生产服务的团队几乎没人能绕开 CloudWatch——它是云上最原生、最全面的监控数据源EC2 的 CPUUtilization、RDS 的 DatabaseConnections、ALB 的 HTTPCode_ELB_5XX_Count、Lambda 的 Invocations 和 Duration、ECS 任务的 MemoryUtilizationPercent……这些指标每天都在产生海量时序数据。但问题来了如果你的监控栈已经统一用 Prometheus Grafana那 CloudWatch 就成了一个“数据孤岛”。你没法直接在 Grafana 里写aws_ec2_cpu_utilization{instance_idi-0a1b2c3d4e5f67890}这样的 PromQL 查询更没法把 CloudWatch 指标和你自定义的 JVM 指标比如jvm_memory_used_bytes放在同一张看板里做关联分析。这时候官方方案是 CloudWatch Exporter for Prometheus由 AWS Labs 维护但它用的是 Go 编写配置偏静态对 Java 技术栈团队来说调试、定制、打 patch 都不够“顺手”。而我们今天聊的这个项目就是为 Java 工程师量身打造的一套解法它不是一个黑盒二进制而是一个可读、可调、可 debug、可嵌入、可扩展的 Java 工程。它不替代 CloudWatch而是做它的“翻译官”——把 CloudWatch API 返回的 JSON 响应实时转换成 Prometheus 的/metrics端点能吐出的文本格式即 OpenMetrics 格式。你不需要改 Prometheus 配置去对接 AWS只需要把它当成一个标准的 exporter 加进scrape_configs剩下的交给它。关键词里的“CloudWatch导出器”不是泛泛而谈它特指一种协议桥接层一边是 AWS SDK v2 的异步 HTTP 客户端另一边是 Prometheus Java Client 的 CollectorRegistry中间是 YAML 驱动的指标发现逻辑与采样调度引擎。而“Prometheus Java客户端”这个标签恰恰点出了它的技术底色——它没有自己造轮子去实现指标注册、Gauge/Counter 封装、HTTP 暴露逻辑而是深度集成io.prometheus:simpleclient_httpserver和io.prometheus:simpleclient_hotspot这意味着它天生支持 JVM 自身指标GC 次数、线程数、堆内存使用率的自动暴露你甚至可以在同一个端口下同时看到cloudwatch_cpu_utilization和jvm_gc_collection_seconds_count这对排查“到底是业务慢还是 JVM 卡顿”这类混合故障特别关键。至于“AWS指标采集”它解决的从来不是“能不能采”而是“怎么采得稳、采得准、采得省”。比如你不会想让 exporter 每 15 秒就全量 ListMetrics 一次那会触发 API 限流也不会希望所有 EC2 实例的 NetworkIn 指标都用 1 分钟粒度去 GetMetricStatistics成本翻倍且无意义更不想因为某个命名空间里突然多了几百个新指标就把整个 exporter 的内存撑爆。这个 Java 导出器的设计哲学就是把“可控性”刻进每一行代码采样间隔、统计周期、维度过滤、并发请求数、失败重试策略、缓存 TTL……全部可配、可观察、可降级。它不是一个“开箱即用就完事”的玩具而是一个你愿意放进 CI/CD 流水线、写单元测试、加 Jaeger 链路追踪、甚至在生产环境里开着 JFR 录制 GC 行为的正经服务。我去年在给一家做跨境支付的客户做监控体系升级时就踩过没这种导出器的坑。他们当时用的是 Python 写的简易脚本轮询 CloudWatch结果某天 RDS 命名空间新增了 200 个 Performance Insights 指标脚本没做任何限流和缓存直接把 CloudWatch API 调用频率干到每秒 80 次触发了账户级限流连带影响了 CloudTrail 日志投递。后来换成这个 Java 版本光靠配置里的cache_ttl: 3005 分钟缓存 ListMetrics 结果和max_concurrent_requests: 5两个参数就把峰值 QPS 压到了 3 以下而且还能通过/actuator/prometheus端点实时看到cloudwatch_list_metrics_total和cloudwatch_get_metric_statistics_errors_total这类内部指标真正做到了“可观测即运维”。2. 整体架构与设计思路拆解这个导出器不是简单地把 AWS SDK 调用封装成一个 HTTP handler它的核心价值在于三层抽象发现层Discovery→ 采集层Collection→ 暴露层Exposition。这三层之间完全解耦靠事件驱动和配置驱动串联这也是它能兼顾灵活性与稳定性的根本原因。2.1 发现层动态指标发现而非静态硬编码很多初版 CloudWatch 导出器会要求你在 YAML 里手动列出所有要采集的指标比如metrics: - namespace: AWS/EC2 metric_name: CPUUtilization dimensions: - name: InstanceId value: i-0a1b2c3d4e5f67890这在测试环境没问题但一上生产就崩EC2 实例是自动伸缩组ASG动态创建的RDS 实例可能按周滚动重建ALB 名字更是随 CI/CD 流水线生成。硬编码等于放弃自动化。这个 Java 版本的破局点是引入了CloudWatch ListMetrics API 的智能分页与缓存机制。它的工作流程是这样的1. 启动时根据配置中的namespaces列表如[AWS/EC2, AWS/RDS]并发发起ListMetricsRequest2. 对每个命名空间它不是一次性拉取全部指标CloudWatch 默认只返回 500 条而是自动处理分页令牌nextToken直到遍历完该命名空间下所有指标3. 关键来了它会对返回的指标列表做两层过滤——先按metric_name白名单如[CPUUtilization, NetworkIn, DatabaseConnections]筛再按dimensions模式匹配支持通配符*和正则表达式比如dimension_pattern: ^(InstanceId|DBInstanceIdentifier)$4. 最终生成一个内存中的MetricDefinition清单每条记录包含完整命名空间、指标名、维度组合如[{name:InstanceId,value:i-0a1b2c3d4e5f67890}]、统计方式Average、周期300秒等元信息5. 这个清单会被写入一个带 TTL 的 Guava Cache默认 5 分钟后续采集请求都从缓存读避免高频 ListMetrics。提示example.yml里discovery.cache_ttl: 300这个参数不是随便设的。CloudWatch 的指标元数据变更频率很低通常以小时计设成 300 秒既能保证新鲜度又能把 ListMetrics 调用频次压到最低。如果你的业务有“分钟级动态创建资源”的场景比如 Fargate 任务可以调小到 60但务必配合discovery.max_retries: 2防止缓存击穿。2.2 采集层异步、批处理、带背压的指标拉取发现只是第一步真正的性能瓶颈在采集。CloudWatch 的GetMetricStatistics是一个昂贵的 API每次调用最多只能查 1 个指标 1 组维度 1 个统计周期且返回的数据点数量受时间范围限制最长 14 天最多 1440 个点。如果对 100 个 EC2 实例的 CPU 指标逐个串行调用耗时会轻松突破 30 秒远超 Prometheus 默认 10 秒抓取超时。这个 Java 导出器的应对策略是“三管齐下”-异步非阻塞底层用的是 AWS SDK v2 的CloudWatchAsyncClient所有GetMetricStatistics调用都是 CompletableFuture 异步发起线程不阻塞-批量合并它会把同一命名空间、同一指标名、同一统计方式、同一周期的请求按维度值进行哈希分组比如所有AWS/EC2/CPUUtilization的请求按InstanceId分成 N 组然后对每组维度并发发起请求最大并发数由collection.max_concurrent_requests控制默认 10-智能背压当CompletableFuture.allOf()检测到某批次请求中超过 30% 失败或平均响应时间超过 5 秒它会自动触发降级——跳过本次采集复用上一轮缓存的指标值并记录cloudwatch_collection_degraded_total计数器。这里有个容易被忽略的细节collection.sample_interval采样间隔和cloudwatch.periodCloudWatch 数据周期不是一回事。前者是 exporter 主动发起采集的频率比如每 60 秒拉一次最新数据后者是你要从 CloudWatch 获取哪个粒度的历史数据比如period: 300表示查最近 5 分钟的平均值。它们的关系是sample_interval应该 ≥period否则会出现数据覆盖或空洞。example.yml里设的sample_interval: 60和period: 300意味着它每分钟去 CloudWatch 查一次“过去 5 分钟的平均 CPU 使用率”这样既能保证数据新鲜延迟 ≤ 60 秒又不会因频繁查询小周期数据而推高成本。2.3 暴露层原生 Prometheus 兼容不止于指标暴露层看似最简单不就是启动一个 HTTP Server 吗但恰恰是它决定了你能否真正融入现有监控生态。这个导出器用的是io.prometheus:simpleclient_httpserver但它做了两处关键增强第一多端点支持除了标准的/metrics暴露 CloudWatch 指标 JVM 指标它还内置了/health返回{ status: UP, cloudwatch: UP }和/actuator/prometheusSpring Boot Actuator 风格兼容 Prometheus Operator 的 ServiceMonitor 探针。更重要的是它把自身运行时的关键指标也注册进了 CollectorRegistry-cloudwatch_discovery_duration_secondsListMetrics 耗时直方图-cloudwatch_collection_duration_secondsGetMetricStatistics 耗时直方图-cloudwatch_collection_errors_total按错误类型ThrottlingException,InvalidParameterValue,AccessDenied分组的计数器-exporter_uptime_seconds进程启动时长Gauge第二指标命名与标签规范化CloudWatch 原生指标名带斜杠如AWS/EC2/CPUUtilizationPrometheus 不允许所以它自动转换为下划线aws_ec2_cpu_utilization。维度值如InstanceIdi-0a1b2c3d4e5f67890会被转为 Prometheus 标签但会做安全清洗去掉非法字符/,:, 长度截断默认 64 字节并添加cloudwatch_dimension_前缀避免和业务标签冲突。example.yml里的label_prefix: cw_就是控制这个前缀的。注意如果你启用了aws_tag_select功能通过tag:GetResources权限获取资源标签它会把Nameprod-db,Environmentprod这类标签也作为 Prometheus 标签注入但会强制加上tag_前缀如tag_Nameprod-db这是为了和原始维度标签严格区分开防止 label cardinality 爆炸。3. 核心配置解析与实操要点YAML 配置是这个导出器的“大脑”它不像某些工具那样只有几个开关而是提供了一套完整的指标治理 DSL。我们来逐段拆解example.yml的真实含义并告诉你哪些参数必须改、哪些可以不动、哪些改了会踩大坑。3.1 全局配置端口、认证、日志server: port: 9106 context_path: / aws: region: us-east-1 credentials: type: default # 可选: default, static, profile, container # static 下才需要 access_key_id / secret_access_key # profile 下才需要 profile_nameserver.port是最直观的但context_path很关键。如果你打算用 Nginx 做反向代理比如把https://monitor.example.com/cloudwatch/代理到后端http://exporter:9106/就必须把context_path设为/cloudwatch/否则/metrics请求会 404。我见过太多人卡在这一步最后发现是路径没对齐。aws.credentials.type是认证模式开关。default表示走 AWS 标准凭证链推荐它会按顺序检查1. 环境变量AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY2. ECS Task Role 或 EC2 Instance ProfileIAM 角色3.~/.aws/credentials文件里的defaultprofile4.~/.aws/config文件里的defaultprofile带region如果你在 Kubernetes 里部署强烈建议用 IAM Roles for Service AccountsIRSA即让 Pod 的 ServiceAccount 绑定一个 IAM Role这样连环境变量都不用设最安全。type: static只应在开发测试时用生产环境绝对禁止硬编码 AKSK。3.2 发现配置精准定位你要的指标discovery: cache_ttl: 300 max_retries: 2 namespaces: - AWS/EC2 - AWS/RDS - AWS/ELB metric_names: - CPUUtilization - NetworkIn - DatabaseConnections - HTTPCode_ELB_5XX_Count dimension_patterns: - ^(InstanceId|DBInstanceIdentifier|LoadBalancerName)$这段配置决定了“导出器知道哪些指标存在”。namespaces和metric_names是白名单必须精确匹配 CloudWatch 控制台里看到的名字大小写敏感。dimension_patterns是正则表达式用来筛选维度名。注意它匹配的是维度名InstanceId不是维度值i-0a1b2c3d4e5f67890。如果你想只采集特定实例应该在collection.metrics里用dimensions字段硬编码而不是在这里过滤。max_retries: 2是防抖关键。ListMetrics 在高并发下偶尔会返回ThrottlingException设成 2 次重试能极大提升发现成功率。但别设成 5否则一次失败发现会拖慢整个启动过程。3.3 采集配置定义如何拉取、聚合、暴露指标collection: sample_interval: 60 metrics: - namespace: AWS/EC2 metric_name: CPUUtilization statistics: [Average] period: 300 dimensions: - name: InstanceId value: * aws_tag_select: - Name - Environment这才是真正的“业务逻辑”。我们逐字段看sample_interval: 60Exporter 每 60 秒触发一次采集循环。它和 Prometheus 的scrape_interval是独立的但建议保持一致比如都设 60s否则会出现数据点对不齐。statistics: [Average]CloudWatch 支持Average,Sum,Maximum,Minimum,SampleCount五种统计方式。Average最常用但对计数类指标如Invocations你应该用Sum否则会得到“平均每秒调用次数”而不是“总调用次数”。period: 300告诉 CloudWatch “我要查过去 5 分钟的数据”。这个值必须是 CloudWatch 支持的周期60, 300, 3600, 86400 秒不能写 120 或 600。dimensions是灵魂所在。value: *表示通配所有该维度的值比如所有 EC2 实例这是动态发现的基础。但要注意*通配符只在discovery阶段生效在collection阶段它会被替换成实际发现的维度值列表。如果你写value: i-0a1b2c3d4e5f67890那就是静态采集失去了自动化意义。aws_tag_select启用后它会调用tag:GetResourcesAPI根据当前指标关联的资源 ARN如arn:aws:ec2:us-east-1:123456789012:instance/i-0a1b2c3d4e5f67890去查标签。[Name, Environment]表示只注入这两个标签。实测下来这个 API 调用比GetMetricStatistics还贵所以千万别写[*]那会把所有标签都拉下来cardinality 直接爆炸。3.4 高级配置为生产环境兜底advanced: collection: max_concurrent_requests: 10 timeout_seconds: 10 discovery: max_concurrent_namespaces: 3 jvm: expose_hotspot_metrics: truemax_concurrent_requests: 10这是并发控制的闸门。CloudWatch 默认每账户每区域 50 QPS但这是所有 API 的总和。如果你的导出器占了 10 并发其他服务比如 Terraform Apply就只剩 40。设成 10 是平衡点既能保证采集速度又留足余量。如果发现cloudwatch_collection_errors_total{error_typeThrottlingException}持续上升就该调低它。timeout_seconds: 10GetMetricStatistics的单次超时。CloudWatch 在数据量大时可能响应慢设成 10 秒比默认的 30 秒更激进能更快失败、更快重试避免线程池被占满。max_concurrent_namespaces: 3ListMetrics 是跨命名空间并发的设成 3 意味着最多同时查 3 个命名空间如AWS/EC2,AWS/RDS,AWS/ELB避免瞬间打满 CloudWatch 的 ListMetrics 限额默认 5 QPS。expose_hotspot_metrics: true开启 JVM 自身指标。它会自动注册jvm_memory_pool_used_bytes,jvm_threads_current,jvm_gc_pause_seconds等 30 个指标。这些指标和 CloudWatch 指标在同一端点暴露让你一眼看出“是不是 GC 导致了指标采集延迟”。4. 实操过程与核心环节实现现在我们来走一遍从零开始把这个导出器跑起来的完整流程。我会以 Linux/macOS 为例Windows 用户请自行替换mvn和java命令路径。4.1 环境准备与构建首先确认 Java 版本java -version # 输出应为openjdk version 11.0.20 2023-01-17 # 或 openjdk version 17.0.8 2023-07-18 # Java 8 也能跑但强烈建议用 11因为 AWS SDK v2 要求 Java 11然后克隆仓库假设你已下载 ZIP 并解压cd LFkjCRQ8yVqubXToKTFY-master-9a89cf9d111efdf32df00d30cbe82541a833ab8b ls -la # 你会看到 pom.xml, src/, example.yml, Dockerfile 等构建 JAR 包Maven 3.8mvn clean package -DskipTests # -DskipTests 是为了加速生产环境部署前务必去掉跑一遍 test # 构建成功后target/ 目录下会出现 cloudwatch-exporter-1.0.0.jar实操心得第一次构建可能会慢因为要下载 AWS SDK v2、Prometheus Client、SLF4J 等依赖。建议提前执行mvn dependency:go-offline把依赖下全。另外pom.xml里maven.compiler.source和maven.compiler.target都设为11如果你强行用 Java 8 编译会报Unsupported class file major version 61错误。4.2 本地运行与验证在本地运行前先配置 AWS 凭证。最安全的方式是用~/.aws/credentialsmkdir -p ~/.aws cat ~/.aws/credentials EOF [default] aws_access_key_id YOUR_ACCESS_KEY_ID aws_secret_access_key YOUR_SECRET_ACCESS_KEY EOF chmod 600 ~/.aws/credentials然后启动导出器java -jar target/cloudwatch-exporter-1.0.0.jar \ --config-file example.yml \ --port 9106 # --config-file 指定 YAML 配置--port 覆盖 server.port启动后你会看到类似日志INFO c.c.CloudWatchExporterApplication - Started CloudWatchExporterApplication in 3.212 seconds (JVM running for 3.724) INFO c.c.d.CloudWatchDiscoveryService - Discovered 12 metrics in namespace AWS/EC2 INFO c.c.d.CloudWatchDiscoveryService - Discovered 8 metrics in namespace AWS/RDS立刻验证curl -s http://localhost:9106/metrics | head -20 # 你应该看到 # # HELP cloudwatch_cpu_utilization Average CPU utilization percentage # # TYPE cloudwatch_cpu_utilization gauge # cloudwatch_cpu_utilization{cw_namespaceAWS/EC2,cw_metric_nameCPUUtilization,cw_statisticAverage,cw_period300,InstanceIdi-0a1b2c3d4e5f67890} 12.34 # # HELP jvm_memory_used_bytes Used bytes of a given JVM memory area. # # TYPE jvm_memory_used_bytes gauge # jvm_memory_used_bytes{areaheap} 1.23456789E8注意head -20只是快速确认格式真正要看全量指标用curl -s http://localhost:9106/metrics | grep cloudwatch_ | wc -l统计行数。如果返回 0大概率是example.yml里的namespaces或metric_names写错了或者你的 AWS 凭证没权限检查cloudwatch:ListMetrics是否授权。4.3 Docker 部署生产就绪的容器化方案项目自带Dockerfile构建镜像只需一行docker build -t cloudwatch-exporter:1.0.0 .Dockerfile的关键点- 基础镜像是eclipse-jetty:11-jre11-slim轻量、安全、Java 11 原生支持- 把target/cloudwatch-exporter-1.0.0.jarCOPY 进镜像- 暴露9106端口- 启动命令是java -jar /app.jar --config-file /config.yml运行容器以 ECS 为例docker run -d \ --name cw-exporter \ -p 9106:9106 \ -v $(pwd)/example.yml:/config.yml:ro \ -e AWS_REGIONus-east-1 \ -e AWS_ACCESS_KEY_IDYOUR_KEY \ -e AWS_SECRET_ACCESS_KEYYOUR_SECRET \ cloudwatch-exporter:1.0.0但在生产环境绝不要用-e AWS_ACCESS_KEY_ID正确姿势是EC2 实例给实例配置 IAM Role容器内自动继承凭证ECS Fargate在 Task Definition 的taskRoleArn字段指定 RoleEKS Pod用 IRSAServiceAccount 注解eks.amazonaws.com/role-arn。验证容器内指标docker exec cw-exporter curl -s http://localhost:9106/health # 返回 {status:UP,cloudwatch:UP}4.4 Prometheus 集成让它真正工作起来在 Prometheus 的prometheus.yml中添加 jobscrape_configs: - job_name: cloudwatch-exporter static_configs: - targets: [cloudwatch-exporter:9106] # 如果和 Prometheus 同 Docker 网络 # 或 targets: [host.docker.internal:9106] # macOS/Windows Docker Desktop # 或 targets: [192.168.1.100:9106] # Linux 直接 IP metrics_path: /metrics scheme: http scrape_interval: 60s scrape_timeout: 30s重启 Prometheus 后访问http://prometheus/targets你应该看到这个 job 是UP状态并且Labels列显示instancecloudwatch-exporter:9106。最后写一条 PromQL 验证avg by (InstanceId) (cloudwatch_cpu_utilization{cw_namespaceAWS/EC2, cw_metric_nameCPUUtilization})如果返回了多个InstanceId的平均值恭喜你已经打通了 AWS 监控数据到 Prometheus 的最后一公里。5. 常见问题与排查技巧实录在真实生产环境中这个导出器最常见的问题不是“跑不起来”而是“跑得不稳”或“数据不准”。我把过去两年帮客户排障的经验浓缩成一张速查表并附上独家调试技巧。5.1 常见问题速查表问题现象可能原因快速验证命令解决方案/metrics返回空或只有 JVM 指标没有cloudwatch_开头的指标discovery阶段失败没发现任何指标curl -s http://localhost:9106/actuator/prometheus | grep cloudwatch_discovery_total检查example.yml的namespaces和metric_names是否拼写正确检查 AWS 凭证是否有cloudwatch:ListMetrics权限查看日志中Discovered X metrics行数/metrics有指标但数值一直是0或NaNcollection阶段GetMetricStatistics返回空数据点curl -s http://localhost:9106/actuator/prometheus | grep cloudwatch_collection_duration_seconds_count检查period是否超出 CloudWatch 数据保留期EC2 默认 15 个月但免费层只存 2 周检查dimensions的value是否匹配真实资源 ID用 AWS CLI 手动验证aws cloudwatch get-metric-statistics --namespace AWS/EC2 --metric-name CPUUtilization --dimensions NameInstanceId,Valuei-xxx --start-time $(date -d 5 minutes ago %s) --end-time $(date %s) --period 300 --statistics Average --output jsonPrometheus 抓取失败状态为DOWN导出器 HTTP Server 崩溃或网络不通curl -I http://localhost:9106/health应返回 200netstat -tuln \| grep 9106检查导出器日志是否有OutOfMemoryError增加 JVM 参数java -Xms512m -Xmx1024m -jar ...检查防火墙是否放行 9106 端口指标标签过多Prometheus 存储暴涨aws_tag_select启用了[*]或维度值本身含高基数字段如RequestIdcurl -s http://localhost:9106/metrics \| grep cloudwatch_cpu_utilization{ \| head -1立即修改example.yml将aws_tag_select改为明确的[Name, Environment]在dimensions中避免使用RequestId、TraceId等唯一 ID 类维度导出器 CPU 使用率持续 100%discovery或collection并发过高或 GC 频繁jstat -gc pidcurl -s http://localhost:9106/actuator/prometheus \| grep jvm_gc_降低advanced.collection.max_concurrent_requests至 5增加jvm.expose_hotspot_metrics: true后用 Grafana 看jvm_gc_pause_seconds_max是否异常考虑升级到 Java 17ZGC 更友好5.2 独家调试技巧三步定位根因第一步打开 DEBUG 日志默认日志级别是 INFO看不到详细请求。启动时加参数java -Dlogging.level.com.cloudwatchDEBUG -jar target/cloudwatch-exporter-1.0.0.jar --config-file example.yml你会看到类似DEBUG c.c.c.CloudWatchCollectionService - Sending GetMetricStatisticsRequest for metric: AWS/EC2/CPUUtilization, dimensions: [{nameInstanceId, valuei-0a1b2c3d4e5f67890}] DEBUG c.c.c.CloudWatchCollectionService - GetMetricStatisticsResponse received, data points count: 12这能直接确认请求发出去了没CloudWatch 返回数据了没数据点数量对不对第二步用/debug/metrics端点看内部状态这个端点不在公开文档里但代码里埋了DebugMetricsEndpoint类。访问curl -s http://localhost:9106/debug/metrics返回 JSON包含discovery_cache_size: 当前缓存的指标数collection_queue_size: 待采集的指标队列长度http_client_active_requests: 当前活跃的 HTTP 请求连接数jvm_memory_committed_bytes: JVM 已提交内存如果collection_queue_size持续 100说明采集跟不上发现速度要调大max_concurrent_requests或调小sample_interval。第三步用 JMX 深度诊断Java 11导出器集成了io.prometheus:simpleclient_hotspot它会自动暴露 JMX Bean。用jconsole连接jconsole localhost:9106在MBeans标签页展开java.lang→Memory→Attributes看HeapMemoryUsage.used展开com.cloudwatch→Collector→Attributes看LastCollectionTime毫秒时间戳计算距今多久就能判断采集是否卡住。实操心得我在给一家游戏公司做支持时遇到过一个诡异问题导出器内存稳定在 800MB但jvm_memory_used_bytes{areaheap}却显示 2GB。最后发现是simpleclient_hotspot的BufferPoolMXBean采集逻辑有 bug它把 Direct Buffer 内存也算进了 heap。解决方案是关掉expose_hotspot_metrics: false改用jvm_direct_*指标单独监控。这个坑只有真刀真枪跑过一周以上才能踩到。6. 二次开发与定制化扩展这个项目之所以被称作“适合二次定制开发”是因为它的源码结构清晰模块职责单一且预留了大量 Hook 点。我来分享三个最实用的定制场景以及对应的代码修改路径。6.1 场景一添加自定义指标处理器比如把 NetworkIn 字节转为 MbpsCloudWatch 的NetworkIn单位是字节Prometheus 里直接暴露cloudwatch_network_in_bytes不够直观。你想暴露成cloudwatch_network_in_mbps公式是bytes * 8 / 1000 / 1000 / period。修改步骤1. 在src/main/java/com/cloudwatch/collector/下新建NetworkInMbpsProcessor.java2. 实现MetricProcessor接口重写process方法java public class NetworkInMbpsProcessor implements MetricProcessor { Override public void process(MetricFamilySamples.Sample sample, double value) { // value 是原始字节数sample.labelValues 包含维度 double mbps value * 8 / 1_000_000 / 300; // 除以 period300 秒 sample.value mbps; sample.name cloudwatch_network_in_mbps; // 覆盖指标名 } }3. 在CloudWatchCollectionService的collectMetrics方法里对metricName.equals(NetworkIn)的指标插入这个 processor。这样你不用改任何配置只要在example.yml里把metric_name: NetworkIn的项加上processor: network_in_mbps就能自动生效。6.2 场景二对接企业内部 CMDB用主机名替换 InstanceId很多企业 CMDB 里InstanceIdi-0a1b2c3d4e5f67890对应的主机名是web-prod-01。你想把 Prometheus 标签里的InstanceIdi-0a1b2c3d4e5f67890替换为hostnameweb-prod-01。修改步骤1. 在src/main/java/com/cloudwatch/dimension/下新建CmdbDimensionResolver.java2. 实现DimensionResolver接口提供resolve(String instanceId)方法内部调用 CMDB REST API3. 在CloudWatchDiscoveryService的discoverMetrics方法里对dimension.name InstanceId的维度值调用cmdbResolver.resolve(value)获取主机名并存入MetricDefinition.dimensions。这样example.yml里只需加一行dimension_resolver: cmdb所有InstanceId标签就自动变成可读的主机名。6.3 场景三添加告警指标比如 CloudWatch API 调用失败率 5%你想在 Grafana 里画一张图显示“过去 5 分钟 CloudWatch API 失败率”并设置告警。修改步骤1. 在src/main/java/com/cloudwatch/metrics/下新建CloudWatchApiErrorRateCollector.java2. 继承Collector类重写collect方法从cloudwatch_collection_errors_total和cloudwatch_collection_total计数器中计算比率3. 在CloudWatchExporterApplication的main方法里调用collectorRegistry.register(new CloudWatchApiErrorRateCollector())。最终/metrics端点就会多出# HELP cloudwatch_api_error_rate CloudWatch API error rate in last 5 minutes # TYPE cloudwatch_api_error_rate gauge cloudwatch_api_error_rate 0.023然后你就可以在 Prometheus 里写告警规则- alert: CloudWatchAPIFailureRateHigh expr: cloudwatch_api_error_rate 0.05 for: 5m labels: severity: warning annotations: summary: CloudWatch API failure rate is high我个人在实际操作中的体会是这个导出器最大的价值不是它“现在能做什么”而是它“将来能很容易地做什么”。它的 Maven 模块划分core,collector,discovery,http让每个功能都像乐高积木一样可插拔。我见过最猛的定制是把整个discovery模块替换成从 Datadog API 拉取指标定义从而实现了 CloudWatch Datadog 双源指标统一暴露。只要你理解了它的三层抽象发现→采集→暴露任何监控数据源的接入本质上都是在discovery模块里写一个新的XXXDiscoveryService在collector模块里写一个新的XXXCollector然后注册进去——就这么简单。本文还有配套的精品资源点击获取简介一个用Java开发的轻量级工具能把AWS CloudWatch里的各种监控指标比如EC2 CPU使用率、RDS连接数、Lambda调用次数等实时转换成Prometheus能直接抓取的格式。支持Java 8编译用mvn package运行就一条java -jar命令指定端口和YAML配置文件自带example.yml参考默认走9106端口。认证走AWS标准凭证链环境变量AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY、IAM角色、~/.aws/credentials文件都行最小权限只要cloudwatch:ListMetrics和cloudwatch:GetMetricStatistics如果要用标签筛选功能aws_tag_select再加个tag:GetResources就行。配置全靠YAML可以按命名空间、指标名、维度如InstanceId、LoadBalancerName、统计方式Average/Sum/Maximum、采样周期比如5分钟和抓取间隔灵活设置。带Dockerfilebuild完就能docker run跑起来项目结构规范含完整Maven配置pom.xml、源码src/main、测试src/test、许可证LICENSE、说明文档README.md、贡献指南CONTRIBUTING.md和安全策略SECURITY.md适合嵌入现有Prometheus监控体系也方便二次开发或定制指标采集逻辑。本文还有配套的精品资源点击获取

相关新闻