2018年6月25日 工程

通过 Metricbeat 和 Jolokia 监测 Java 应用

作者 Jaime Soriano

Java 虚拟机 (JVM) 的一大特点就是为操作管理和监测提供了一套完整框架。在这篇文章中,我们将会了解什么是 JMX(Java 管理扩展),如何探索其所暴露出来的信息,以及如何借助 Jolokia 和 Elastic Stack 来充分利用 JMX。如果已经熟悉 JMX 和 Jolokia,您可以跳过第一部分,直接前往后续部分了解 Metricbeat 相关功能的详细信息。

JMX 和 JConsole

JMX 技术定义了完整的架构和设计模式集合,以对 Java 应用进行监测和管理。JMX 的基础是托管豆(managed bean,业内更习惯将其称为 MBean),MBean 是通过依赖注入完成实例化的各个类别,代表着 JVM 中的资源。由于 MBean 代表 JVM 中的资源,所以我们可以用其来管理应用的特定方面,或者更为常见的一种做法,用其来收集与这些资源的使用相关的统计数据。JMX 的核心是 MBean 服务器,此类服务器可以作为媒介将 MBean、同一 JVM 内的应用以及外部世界联系在一起。与 MBean 之间的任何交互都是通过此服务器完成的。通常而言,只有 Java 代码能够直接访问 JMX API,但是有一些适配器可将该 API 转换为标准协议,例如 Jolokia 便可将其转换为 HTTP。JConsole 是一款可与 JMX 配合使用的实用工具,在 Java Runtime 的普通发布版本中就包括这一工具。打开之后,您首先会看到设备上运行的 Java 进程列表。如果没有运行任何 Java 进程,您至少会看到 JConsole 自身。

JConsole welcome screen

当连接至这些进程中的任何一项时,会打开一个包含多个选项卡的窗口,此窗口提供了关于进程不同方面(例如内存或线程)的一般性监测信息。其中还有一个选项卡上列出了 MBean 浏览器。

JConsole main window

在此浏览器中,您可以看到进程的 MBean 列表,并且此列表会按照命名空间进行分组。某些 Mbean(例如 java.lang 命名空间下的这些)可以在 JVM 中找到,然而其他 MBean 却是针对具体应用的。对于每个 MBean,您可以看到属性、操作和通知等类别。对于监测而言,我们将会专注于属性。有一些这样的 MBean:尽管它们各不相同,但是却会实施相同的界面。举例说明,我们经常见到不同的应用(例如 Tomcat 或 Kafka)会根据用例使用不同的垃圾回收器,但是在 JMX 中,这些都是同一类型的对象,只是名称不同而已。

MBean browser in JConsole

能够发现全部这些信息固然很好,但是在监测基础架构时,通常并不会提供诸如 JConsole 等工具。此外,您可能还需要汇集来自不同进程(可能位于不同的服务器中)的信息。幸运的是,借助 Metricbeat 和 Jolokia 便能应对这一问题。

使用 Metricbeat 和 Jolokia 收集 JMX 指标

Metricbeat 可以从不同的服务器收集信息,并将这些信息传送给 Elasticsearch,然后您借助 Kibana 便可通过多种方式对其进行可视化了。但是如我们之前看到的那样,JMX 只能用于 Java 应用,这时 Jolokia 就可以发挥巨大作用了。Jolokia 是一个可以在 JVM 上进行部署的代理,可以通过一个 REST-like HTTP 端点来暴露这些 JVM 上的 MBean,并让在同一主机上运行的非 Java 应用轻松使用这些信息。
既可以将 Jolokia 作为普通的 JVM 代理进行部署,也可以在 Java EE 环境中作为 WAR 进行部署,还可以作为 OSGI 或 Mule 代理进行部署。如要将 Jolokia 作为代理进行部署,您在启动 Java 应用时必须使用 -javaagent 旗标。如果直接使用 Java 命令,您可以将其作为参数直接传递,但是某些应用会有其独自的启动脚本,并在其文档中说明如何通过不同方式实现这一点。举例说明,如果使用配有独自启动脚本的 Kafka,您可能需要利用 KAFKA_OPTS 环境变量:

export KAFKA_OPTS=-javaagent:/opt/jolokia-jvm-1.5.0-agent.jar=port=8778,host=localhost
./bin/kafka-server-start.sh ./config/server.properties

如要将 Jolokia 作为 WAR 进行部署,则代理必须安装在 Java EE 服务器中。举例说明,在 Tomcat 中,将 WAR 文件复制粘贴到对应的 webapps 目录中,便可完成部署。对于每个应用,我们建议您查看相应文档以确定运行 Java 代理的最佳方式。我们还建议您查看 Jolokia 文档以了解有哪些代理可供选择以及各自的选项。如果不能在同一 JVM 中部署 Jolokia,Jolokia 还有代理服务器模式,通过这种方式,您便可使用其他 JVM 来查询 JMX。Jolokia 一旦开始在应用的 JVM 中运行,通过使用 Jolokia 模块中的 JMX 指标集合(在 Metricbeat 5.4 中推出),借助 Metricbeat 收集 JMX 指标就很简单了。必须参考主机、Jolokia 代理的端口,以及 JMX 指标和 Metricbeat 事件字段之间的映射集合,来对此 Jolokia 模块进行配置。我们接下来看一个例子。

示例:使用 Metricbeat 和 Jolokia 监测 Java 应用

假设 Jolokia 已经在监测本地主机,端口 8778,也就是前面那个 Kafka 例子中的情形。我们可以使用 JConsole 来查找我们希望监测的 MBean 和属性。选择 MBean 后,我们会看到其名称,可将名称直接复制粘贴到配置文件中。

Threading MBean in JConsole

在这个例子中,我们需要监测来自名为 java.lang:type=Threading 的 MBean 的线程数量,以及来自名为 java.lang:type=Memory 的 MBean 的堆内存使用情况。
需要进行如下配置:

- module: jolokia
  metricsets: ["jmx"]
  hosts: ["localhost:8778"]
  period: 10s
  namespace: "jvm"
  jmx.mappings:
  - mbean: "java.lang:type=Memory"
    attributes:
    - attr: "HeapMemoryUsage"
        field: "memory.heap"
    - attr: "NonHeapMemoryUsage"
        field: "memory.nonheap"
  - mbean: "java.lang:type=Threading"
    attributes:
    - attr: "ThreadCount"
        field: "thread.count"
    - attr: "DaemonThreadCount"
        field: "thread.daemon"

Metricbeat 会定期收集信息并通过四个值来传送事件。您可能已经注意到,在 JConsole 中,内存利用率属性并不是和线程计数一样的普通数值,而是均包含四个字段的对象。Metricbeat 负责对事件中的数据进行重组,所以最后其在 jolokia.jvm 命名空间下包含十个字段:

Event with the collected jolokia metrics

更加高级的配置

您可能希望在不同的事件中均拥有字段。从 Metricbeat 6.3 开始,JMX 映射通过事件设置还可定义字段必须通过哪些方式进行组合。拥有相同值的两个事件会组合在一起。举例说明,下面的配置将会生成两个不同的事件,一个是针对内存字段的事件,另一个是针对线程字段的事件:

  jmx.mappings:
  - mbean: "java.lang:type=Memory"
    attributes:
    - attr: "HeapMemoryUsage"
        field: "memory.heap"
        event: "memory"
    - attr: "NonHeapMemoryUsage"
        field: "memory.nonheap"
        event: "memory"
  - mbean: "java.lang:type=Threading"
    attributes:
    - attr: "ThreadCount"
      field: "thread.count"
      event: "threads"
    - attr: "DaemonThreadCount"
      field: "thread.daemon"
      event: "threads"

在 6.3 版本中推出的另外一项新功能是支持通配符,通过这一功能,我们能够针对多个 MBean 使用单一配置,如果应用包含同一类型的多个实例,或者事先不知道具体名称时,这一功能特别实用。举例说明,如果 Tomcat 拥有多个线程池,我们可以借助额外配置来扩展之前的映射,这样的话,也能够获得每个池的线程数量和连接数量。

  - mbean: "Catalina:name=*,type=ThreadPool"
    attributes:
    - attr: "currentThreadCount"
      field: "thread.count"
    - attr: "maxThreads"
      field: "thread.max"
    - attr: "connectionCount"
      field: "connection.count"

通过这一配置,对于每个匹配的 MBean 均会传送一个新事件,并且会包含 MBean 的名称(作为一个新字段):

Event with the collected Jolokia metrics when using wildcards

为了提供全面的 Jolokia 支持,得益于社区的 贡献,同时也得益于 Jolokia Discovery 的实施,我们在 Metricbeat 6.4 中添加了代理服务器模式,通过这一模式我们能够在更加动态的环境中监测 Java 应用。Jolokia Discovery 是一项基于 UDP 多播的技术,允许 Jolokia 代理发布各自的端点以及它们所关联的服务的更多信息。我们的实施由 autodiscover 框架(我们已经针对 Kubernetes 和 Docker 应用了这一框架)提供支持,这一实施能够基于模板实现全功能动态配置。如果想在之前的示例中利用 Jolokia Discovery,我们可以这样做(简化至仅收集线程计数):

metricbeat.autodiscover:
  providers:
  - type: jolokia
    templates:
    - condition:
        contains:
          jolokia.agent.version: "1.5.0"
      config:
      - module: "jolokia"
        metricsets: ["jmx"]
        hosts: ["${data.jolokia.url}"]
        namespace: "jvm"
        jmx.mappings: 
        - mbean: "java.lang:type=Threading"
          attributes:
          - attr: "ThreadCount"
            field: "thread.count"
    - condition:
        contains:
          jolokia.server.product: "tomcat"
      config:
      - module: "jolokia"
        metricsets: ["jmx"]
        hosts: ["${data.jolokia.url}"]
        namespace: "jvm"
        jmx.mappings:
        - mbean: "Catalina:name=*,type=ThreadPool"
          attributes:
          - attr: "currentThreadCount"
            field: "thread.count"

此配置包含两个模板,一个将会应用于已发现的所有 Jolokia 实例,而另外一个则仅会应用于 Tomcat 实例。您可能已经注意到模板中的配置与之前基本相同,但是针对主机使用了变量。尽管这一功能的开发初衷是与 JMX 指标集合搭配使用,但是您可以自由地通过任何其他创新方式将其与其他模块(甚至与 filebeat)搭配使用。如果希望了解更多信息,请查阅 Jolokia 模块autodiscover 文档,也可在 Discuss 上向我们提出任何问题