JMX Exporter源码解读+生产环境最佳实践+解决其抓取指标超时问题

news2024/12/25 17:31:14

文章目录

  • 背景
  • 第一版配置-查询所有MBean
  • 第二版配置-配置白名单
  • 第三版配置-增加Cache
  • 第四版配置-修改jmx_exorter源码禁用默认jvm导出
  • 第五版配置-基于第四版+excludeObjectNameAttributes
  • 第六版配置-修改jmx_exorter源码+includeObjectNameAttributes配置
    • 基于release-1.0.1分支修改源码
    • 更新配置
  • jmx-exporter源码解读
  • 将改动贡献给jmx_exporter社区
  • 生产环境配置建议

背景

在JMX的快速入门与使用+使用JMX Exporter监控+集成OpenTelemetry 博文中当时提出来一个未解决的问题

目前我们遇到的问题是,includeObjectNames本身配置的MBean很少了,但是偶尔还会出现jmx_exporter抓取metrics的时候会长达几十秒甚至一百秒+的情况,目前没找到原因

我们的系统的动态扩容(增加机器)或缩容(减少机器)机制是

  1. prometheus定时拉取jmx-exporter的数据
  2. grafana根据prometheus中的数据配置报警规则
  3. 当触发报警规则时,通过脚本去调用AWS 相关服务的API来实现机器的上线、下线、开机、关机等操作

可以看到上述即使非常依赖于jmx-exporter上报的数据,如果jmx-exporter总是偶尔有超时导致prometheus收集不到数据,那么会严重影响后续脚本的逻辑。(PS: 请不要问我为什么不直接用K8S而是自己实现?问就是emm…现实情况复杂)

当时这个问题一直困扰DevOps组不能解决,且他们缺少Java相关知识。所以找我帮忙看看能不能尝试解决下问题。以下为解决这个问题的过程的思路的全纪录

首先并不是所有服务器都有这种情况,而是具有某种代码类型的服务器,例如:有几台服务器的代码是用来生成报表+定时执行生成报表任务,这种类型的服务器非常容易出现此问题

第一版配置-查询所有MBean

在花了一些时间了解了我们整体的监控架构之后(整体架构在JMX的快速入门与使用+使用JMX Exporter监控+集成OpenTelemetry一文中可以找到),先去查看了Tomcat的日志,发现里面和jmx-exporter agent相关的异常为Broken pipe和Stream is closed,我判断jmx-exporter agent和otel collector的prometheus-receiver之间的连接由于超时断开引发了此异常。

为什么会超时呢?可能有两个原因

  1. 收集指标太多
  2. prometheus-receiver配置的超时太短

我查看了产品环境服务器上jmx_exporter的配置文件,发现DevOps的配置很简单

rules:
- pattern: ".*"

查询了所有MBean。还有一个问题,由于我们代码中使用Ehcache,每一个Cache都会注册自己的MBean到JMX中,所以Cache非常多的时候,JMX里的MBean是非常多的。但是我们不关心这些MBean。

所以第一次判断是认为Ehcache 注册的MBean太多,导致查询所有指标很慢

第二版配置-配置白名单

基于上面的分析,我决定使用jmx-exporter中的includeObjectNames,指定我们关心的MBean

includeObjectNames:
  - "org.apache.commons.pool2:type=GenericObjectPool,*"
  - "tomcat.jdbc:*"
  - "Catalina:type=Manager,*"

除了这3个MBean, jvm相关的MBean在 jmx-exporter默认导出且不可配置。见源码

JvmMetrics.builder().register(PrometheusRegistry.defaultRegistry);

第二版配置上去之后,问题依旧

第三版配置-增加Cache

继续看文档和官方Issue+Google, 看到了增加rules的cache减少了jmx-exporter的抓取时间这一文章,修改配置如下

includeObjectNames:
  - "org.apache.commons.pool2:type=GenericObjectPool,*"
  - "tomcat.jdbc:*"
  - "Catalina:type=Manager,*"
rules:
  - pattern: 'org.apache.commons.pool2<type=GenericObjectPool, name=(\w+)><>(NumActive)'
    cache: true
  - pattern: 'tomcat.jdbc<name=\"\w+/\w+\", .*><>(NumActive)'
    cache: true
  - pattern: 'Catalina<type=Manager,.*><>(activeSessions)'
    cache: true

并增加了prometheus-receiver的抓取超时时间

receivers:
  prometheus:
    config:
      scrape_configs:
      - job_name: 'otel-collector'
        scrape_interval: 30s
        scrape_timeout: 29s
        static_configs:
        - targets: ['127.0.0.1:12345']

第三版配置上去之后,问题依旧

第四版配置-修改jmx_exorter源码禁用默认jvm导出

既然还是超时,我认为问题可能出在jvm本身收集的指标太多了,一旦jvm负载较高,有可能增加查询MBean的时间,我决定直接将源码中JavaAgent的 JvmMetrics.builder().register(PrometheusRegistry.defaultRegistry);直接注释掉,然后修改配置如下

includeObjectNames:
  - "org.apache.commons.pool2:type=GenericObjectPool,*"
  - "tomcat.jdbc:*"
  - "Catalina:type=Manager,*"
  - "java.lang:type=Memory"
  - "java.lang:type=Threading"
rules:
  - pattern: 'org.apache.commons.pool2<type=GenericObjectPool, name=(\w+)><>(NumActive)'
    cache: true
  - pattern: 'tomcat.jdbc<name=\"\w+/\w+\", .*><>(NumActive)'
    cache: true
  - pattern: 'Catalina<type=Manager,.*><>(activeSessions)'
    cache: true
  - pattern: 'java.lang<type=Memory><HeapMemoryUsage>(used|max)'
    cache: true
    name: jvm_memory_$1_bytes
    type: GAUGE
    help: $1 (bytes) of a given JVM memory area
    labels: {"area":"heap"}
  - pattern: 'java.lang<type=Threading><>ThreadCount'
    cache: true
    type: GAUGE
    help: Current thread count of a JVM
    name: jvm_threads_current

通过name兼容原来源码中导出的jvm参数名字

第四版配置上去之后,问题依旧

第五版配置-基于第四版+excludeObjectNameAttributes

到了这一步,我还是没有深入看过jmx-exporter源码。在文档中看到了excludeObjectNameAttributes参数,于是将MBean中不关心的Attribute都配置上。在这一版的配置中,我没有将所有的MBean的不关心的Attribute都配置上,仅配置了jvm相关的MBean和部分MBean,主要是拿这一版的配置来验证下我的猜想。看看到底是不是jvm的某个MBean的某个属性导致的查询慢,修改配置如下

# for the metrics of the jvm itself, we don't have to declare them, they are automatically exported, see https://groups.google.com/g/prometheus-users/c/2WTZn5Vi4FE
includeObjectNames:
  - "java.lang:type=Memory"
  - "java.lang:type=Threading"
  - "org.apache.commons.pool2:type=GenericObjectPool,*"
  - "tomcat.jdbc:*"
  - "Catalina:type=Manager,*"
excludeObjectNameAttributes:
  "java.lang:type=Memory":
    - "ObjectPendingFinalizationCount"
    - "NonHeapMemoryUsage"
    - "Verbose"
    - "ObjectName"
  "java.lang:type=Threading":
    - "ThreadAllocatedMemorySupported"
    - "ThreadAllocatedMemoryEnabled"
    - "CurrentThreadAllocatedBytes"
    - "ThreadContentionMonitoringEnabled"
    - "ThreadContentionMonitoringSupported"
    - "CurrentThreadCpuTimeSupported"
    - "ObjectMonitorUsageSupported"
    - "SynchronizerUsageSupported"
    - "ThreadCpuTimeEnabled"
    - "TotalStartedThreadCount"
    - "AllThreadIds"
    - "CurrentThreadCpuTime"
    - "CurrentThreadUserTime"
    - "ThreadCpuTimeSupported"
    - "PeakThreadCount"
    - "DaemonThreadCount"
    - "ObjectName"
# using cache parameters to increase performance, note that this parameter only caches bean name expressions to rule computation and not cache metrics. see https://github.com/prometheus/jmx_exporter/tree/release-1.0.1/docs
rules:
  - pattern: 'java.lang<type=Memory><HeapMemoryUsage>(used|max)'
    cache: true
    name: jvm_memory_$1_bytes
    type: GAUGE
    help: $1 (bytes) of a given JVM memory area
    labels: {"area":"heap"}
  - pattern: 'java.lang<type=Threading><>ThreadCount'
    cache: true
    type: GAUGE
    help: Current thread count of a JVM
    name: jvm_threads_current
  - pattern: 'org.apache.commons.pool2<type=GenericObjectPool, name=(\w+)><>(NumActive)'
    cache: true
  - pattern: 'tomcat.jdbc<name=\"\w+/\w+\", .*><>(NumActive)'
    cache: true
  - pattern: 'Catalina<type=Manager,.*><>(activeSessions)'
    cache: true

第五版配置上去之后,问题依旧,不过这次能看到超时时间jmx_scrape_duration_seconds降低了,但是超时频率增加了。

这一版验证了两个猜想

  1. 不是jvm本身的MBean查询引起的超时
  2. 排除掉不需要的MBean属性能够降低jmx_scrape_duration_seconds时间

关键的问题:到底是哪个MBean的哪个属性导致的呢?范围已经缩小到3个MBean了

第六版配置-修改jmx_exorter源码+includeObjectNameAttributes配置

既然范围已经缩小到3个MBean了,我为什么不继续增加excludeObjectNameAttributes配置调查下去呢?因为我觉得,这种配置太麻烦了,如果以后我新监控一个MBean的Attribute,且这个MBean有几百个Attribute,那加这个规则岂不是要累死。所以我决定,修改源码,让jmx-exporter只查我声明的Attribute。在看了源码之后,改动如下

基于release-1.0.1分支修改源码

  1. io.prometheus.jmx.ObjectNameAttributeFilter类中做如下改动
public static final String INCLUDE_OBJECT_NAME_ATTRIBUTES = "includeObjectNameAttributes";
private final Map<ObjectName, Set<String>> includeObjectNameAttributesMap;

private ObjectNameAttributeFilter() {
        excludeObjectNameAttributesMap = new ConcurrentHashMap<>();
        includeObjectNameAttributesMap = new ConcurrentHashMap<>();
    }

io.prometheus.jmx.ObjectNameAttributeFilter#initialize方法中新增

if (yamlConfig.containsKey(INCLUDE_OBJECT_NAME_ATTRIBUTES)) {
      Map<Object, Object> objectNameAttributeMap =
              (Map<Object, Object>) yamlConfig.get(INCLUDE_OBJECT_NAME_ATTRIBUTES);

      for (Map.Entry<Object, Object> entry : objectNameAttributeMap.entrySet()) {
          ObjectName objectName = new ObjectName((String) entry.getKey());

          List<String> attributeNames = (List<String>) entry.getValue();

          Set<String> attributeNameSet =
                  includeObjectNameAttributesMap.computeIfAbsent(
                          objectName, o -> Collections.synchronizedSet(new HashSet<>()));

          attributeNameSet.addAll(attributeNames);
          for (String attribueName : attributeNames) {
              attributeNameSet.add(attribueName);
          }

          includeObjectNameAttributesMap.put(objectName, attributeNameSet);
 }

以及增加

public boolean include(ObjectName objectName, String attributeName) {
     boolean result = false;

     if (includeObjectNameAttributesMap.size() > 0) {
         Set<String> attributeNameSet = includeObjectNameAttributesMap.get(objectName);
         if (attributeNameSet != null) {
             result = attributeNameSet.contains(attributeName);
         }
     }

     return result;
}
  1. io.prometheus.jmx.JmxScraper#scrapeBean方法中增加一行
if (objectNameAttributeFilter.include(mBeanName, mBeanAttributeInfo.getName())) {
      name2MBeanAttributeInfo.put(mBeanAttributeInfo.getName(), mBeanAttributeInfo);
 }

更新配置

includeObjectNames:
  - "java.lang:type=Memory"
  - "java.lang:type=Threading"
  - "org.apache.commons.pool2:type=GenericObjectPool,*"
  - "tomcat.jdbc:*"
  - "Catalina:type=Manager,*"
includeObjectNameAttributes:
  "Catalina:type=Manager,host=localhost,context=/替换为真正的xxx":
    - "activeSessions"
  "java.lang:type=Memory":
    - "HeapMemoryUsage"
  "java.lang:type=Threading":
    - "ThreadCount"
  "org.apache.commons.pool2:type=GenericObjectPool,name=替换为真正的xxx":
    - "NumActive"
  "tomcat.jdbc:name=\"jdbc/替换为真正的xxx\",type=ConnectionPool,class=org.apache.tomcat.jdbc.pool.DataSource":
# using cache parameters to increase performance, note that this parameter only caches bean name expressions to rule computation and not cache metrics. see https://github.com/prometheus/jmx_exporter/tree/release-1.0.1/docs
rules:
  - pattern: 'java.lang<type=Memory><HeapMemoryUsage>(used|max)'
    cache: true
    name: jvm_memory_$1_bytes
    type: GAUGE
    help: $1 (bytes) of a given JVM memory area
    labels: {"area":"heap"}
  - pattern: 'java.lang<type=Threading><>ThreadCount'
    cache: true
    type: GAUGE
    help: Current thread count of a JVM
    name: jvm_threads_current
  - pattern: 'org.apache.commons.pool2<type=GenericObjectPool, name=(\w+)><>(NumActive)'
    cache: true
  - pattern: 'tomcat.jdbc<name=\"\w+/\w+\", .*><>(NumActive)'
    cache: true
  - pattern: 'Catalina<type=Manager,.*><>(activeSessions)'
    cache: true

将新的jar包以及配置应用到产品环境之后,只抓取几个Attribute的时间只有0.000xxx秒,这才是我们想要的速度和效果。终于把这个问题给解决了。

jmx-exporter源码解读

在改源码以及排查问题过程中,把jmx-exporter的源码看了一遍,其关键流程如下

  1. 使用maven-shade-plugin设置agent的入口点在这里插入图片描述
    一个Agent的入口点的类中需要包括premain方法,关于如何写一个Java Agent,感兴趣的可以自行Google,这里不做过多解释

  2. 入口点JavaAgent类,主要是将要抓取的指标注册到PrometheusRegistry.defaultRegistry然后启动在这里插入图片描述

  3. createHTTPServer最底层方法io.prometheus.metrics.exporter.httpserver.HTTPServer#HTTPServer,核心在于MetricsHandler在这里插入图片描述

  4. io.prometheus.metrics.exporter.common.PrometheusScrapeHandler#handleRequest方法负责实际的请求在这里插入图片描述

  5. scrape的底层方法在这里插入图片描述
    负责真正的MBean查询。Collector接口的实现类是查询JVM各种MBean的类,例如JvmMetrics > JvmThreadsMetrics,这些实现类都会调用io.prometheus.metrics.model.registry.PrometheusRegistry#register(io.prometheus.metrics.model.registry.Collector)添加到collectors中; MultiCollector的实现类是JmxCollector,负责其他MBean的查询。关于Registry可参考prometheus官方java客户端文档

  6. 这里重点关注下MultiCollector的collect方法在这里插入图片描述

  7. doScrape的最终底层方法在这里插入图片描述
    这就是关键位置,这些步骤都是在JMX快速入门博文里提到的查询MBean方法,后面折叠起来的代码部分,是将查询出来的Attribute格式化为prometheus格式,感兴趣的可以看下源码。

以上就是jmx-exporter agent的处理请求的完整流程,感兴趣的可以看看jmx-exporter源码。

将改动贡献给jmx_exporter社区

解决了这问题之后,我觉得这个新增配置对于一些场景还是有用的,于是我决定将新增的includeObjectNameAttributes配置贡献给社区。

该PR的状态可在这里查看

生产环境配置建议

如果你们生产环境没有出现我遇到的问题,建议使用第三版配置即可。如果也遇到了和我一样的问题,建议使用最后一版配置并确保当前使用的jmx-exporter的release版本包含了我的PR

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2241522.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端(3)——快速入门JaveScript

参考&#xff1a; 罗大富 JavaScript 教程 | 菜鸟教程 JavaScript 教程 1. JaveScript JavaScript 简称 JS JavaScript 是一种轻量级、解释型、面向对象的脚本语言。它主要被设计用于在网页上实现动态效果&#xff0c;增加用户与网页的交互性。作为一种客户端脚本语言&#…

人工智能:塑造未来的工作与生活

目录 人工智能技术的应用前景与影响 人工智能的历史与现状 人工智能的应用领域 人工智能的前景与挑战 个人视角&#xff1a;人工智能的应用前景与未来 人工智能在生活中的潜力 面对人工智能带来的挑战 我的观点与建议 结语 人工智能技术的应用前景与影响 随着人工智能…

东土国产自主智能控制器,亮相第七届长三角科技成果交易博览会

近日&#xff0c;第七届长三角科技成果交易博览会&#xff08;以下简称“长三角科交会”&#xff09;在上海汽车会展中心开幕。为展示嘉定新城产业集聚成果&#xff0c;宣传新城核心区投资环境&#xff0c;新城公司连续第六届参加长三角科交会。 在此次展会上&#xff0c;新城…

AUTOSAR_EXP_ARAComAPI的7章笔记(4)

☞返回总目录 相关总结&#xff1a;本地 / 网络多绑定用例总结 7.3.2 本地/网络多绑定用例 在前一节中&#xff0c;我们看到了的一种多绑定特殊变体&#xff0c;现在来看&#xff0c;也可认为是一种真实情况的变体。 假设有一个与上一章节相似的情景&#xff0c;唯一的区别…

ubuntu将firewall-config导出为.deb文件

firewall-config ubuntu是canonial 公司维护的&#xff0c;用wireshark测过&#xff0c;开机会给他们公司发遥测&#xff08;开了ufw阻塞所有连接也一样&#xff0c;canonial在里面把代码改了&#xff09;firewall-config是fedora(爱好者维护&#xff0c;公益版本)自带的防火墙…

LabVIEW中坐标排序与旋转 参见附件snippet程序

LabVIEW中坐标排序与旋转 参见附件snippet程序LabVIEW中坐标排序与旋转 参见附件snippet程序 - 北京瀚文网星科技有限公司 在LabVIEW中处理坐标排序的过程&#xff0c;尤其是按顺时针或逆时针排列坐标点&#xff0c;常见的应用包括处理几何形状、路径规划等任务。下面我将为您…

基于微信小程序的校园超市购物系统设计与实现,LW+源码+讲解

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本超市购物系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&a…

如何使用EasyExcel生成多列表组合填充的复杂Excel示例

作者&#xff1a;Funky_oaNiu 一、&#xff08;需求&#xff09;生成的表格效果&#xff1a;二、搞一个模板文件三、建立对应的表格实体类四、开始填充五、Vue3前端发起请求下载六、官方文档及AI问答 一、&#xff08;需求&#xff09;生成的表格效果&#xff1a; 其中只有顶部…

AdaBoost 二分类问题

代码功能 生成数据集&#xff1a; 使用 make_classification 创建一个模拟分类问题的数据集。 数据集包含 10 个特征&#xff0c;其中 5 个是有用特征&#xff0c;2 个是冗余特征。 数据集划分&#xff1a; 将数据分为训练集&#xff08;70%&#xff09;和测试集&#xff08;3…

权限相关知识

1.Linux权限的概念 在说Linux权限的概念之前我来问大家一个问题&#xff0c;你们觉得什么是权限&#xff1f; 权限平时的体现呢&#xff0c;就比如不是校长的亲戚就不能逛办公室&#xff0c;没充会员的爱奇艺看不了VIP影视剧&#xff0c;没成会员的的蛋糕店拿不到会员价等等等…

Python爬虫项目 | 一、网易云音乐热歌榜歌曲

文章目录 1.文章概要1.1 实现方法1.2 实现代码1.3 最终效果 2.具体讲解2.1 使用的Python库2.2 代码说明2.2.1 创建目录保存文件2.2.2 爬取网易云音乐热歌榜单歌曲 2.3 过程展示 3 总结 1.文章概要 学习Python爬虫知识&#xff0c;实现简单的一个小案例&#xff0c;网易云音乐热…

苍穹外卖-后端部分

软件开发整体介绍 前端搭建 在非中文目录中双击nginx.exe然后浏览器访问localhost即可 后端搭建 基础准备 导入初始文件 使用git进行版本控制 创建本地仓库和远程仓库,提交Git 连接数据库 连接数据库把资料中的文件放入运行即可 前后端联调测试 苍穹外卖项目接口文档…

3D电子商务是什么?如何利用3D技术提升销售转化?

在数字化浪潮席卷全球的今天&#xff0c;网上购物已成为消费者日常生活中不可或缺的一部分。然而&#xff0c;尽管其便捷性无可比拟&#xff0c;但传统电商模式中的“看不见、摸不着”问题始终困扰着消费者与商家。商品是否符合期望、尺寸是否合适、颜色是否真实……这些不确定…

EXCEL延迟退休公式

如图&#xff1a; A B为手工输入 C2EOMONTH(A2,B2*12) D2EOMONTH(C2,IF(C2>DATEVALUE("2025-1-1"),INT((DATEDIF(DATEVALUE("2025-1-1"),C2,"m")4)/4),0)) E2EOMONTH(A2,B2*12IF(EOMONTH(A2,B2*12)>DATEVALUE("2025-1-1"),INT(…

OpenSSL 自签名

参考文档&#xff1a;unigui开发人员工作手册2021 参考文章&#xff1a;保姆级OpenSSL下载及安装教程-CSDN博客 下载 Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions 进入后向下拉找到下载位置&#xff0c;建议下载二进制版本的精简版&#xff0c…

DevOps工程技术价值流:加速业务价值流的落地实践与深度赋能

DevOps的兴起&#xff0c;得益于敏捷软件开发的普及与IT基础设施代码化管理的革新。敏捷宣言虽已解决了研发流程中的诸多挑战&#xff0c;但代码开发仅是漫长价值链的一环&#xff0c;开发前后的诸多问题仍亟待解决。与此同时&#xff0c;虚拟化和云计算技术的飞跃&#xff0c;…

R语言贝叶斯分析:INLA 、MCMC混合模型、生存分析肿瘤临床试验、间歇泉喷发时间数据应用|附数据代码...

全文链接&#xff1a;https://tecdat.cn/?p38273 多模态数据在统计学中并不罕见&#xff0c;常出现在观测数据来自两个或多个潜在群体或总体的情况。混合模型常用于分析这类数据&#xff0c;它利用不同的组件来对数据中的不同群体或总体进行建模。本质上&#xff0c;混合模型是…

Python酷库之旅-第三方库Pandas(218)

目录 一、用法精讲 1021、pandas.DatetimeIndex.inferred_freq属性 1021-1、语法 1021-2、参数 1021-3、功能 1021-4、返回值 1021-5、说明 1021-6、用法 1021-6-1、数据准备 1021-6-2、代码示例 1021-6-3、结果输出 1022、pandas.DatetimeIndex.indexer_at_time方…

从基础到进阶,Dockerfile 如何使用环境变量

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒📝 什么是 Dockerfile 环境变量?🔖1. `ENV` 指令🔖2. `ARG` 指令🔖语法:🔖使用 `ARG` 的例子:📝 如何使用环境变量提高 Dockerfile 的灵活性🔖1. 动态配置环境🔖2. 配置不同的运行环境🔖3. 多…

2002.6 Partitioning the UMLS semantic network.划分 UMLS 语义网络

Partitioning the UMLS semantic network | IEEE Journals & Magazine | IEEE Xplore 问题 统一医学语言系统&#xff08;UMLS&#xff09;语义网络中的语义类型&#xff08;ST&#xff09;在知识表示和应用中存在不足&#xff0c;例如 ST 的组织方式缺乏直观性和可解释性…