jmx的应用

    技术2024-03-09  77

    您看过多少次正在运行的应用程序,并想知道“它在做什么,为什么要花这么长时间?” 在这些时刻,您可能希望您已在应用程序中内置了更多监视功能。 例如,在服务器应用程序中,您可能有兴趣查看排队等待处理的任务的数量和类型,当前正在进行的任务,过去一分钟或一小时的吞吐量统计信息,平均任务处理时间等等。 这些统计信息很容易收集,但是如果没有需要时检索数据的可靠方式,它们就不是很有用。

    您可以通过多种方式导出操作数据-您可以将定期的统计信息快照写入日志文件,创建Swing GUI,使用在网页上显示统计信息的嵌入式HTTP服务器或发布可以使用的Web服务。查询应用程序状态。 但是在缺少监视和数据发布基础结构的情况下,大多数应用程序开发人员并没有花那么多精力,导致对应用程序工作情况的可见性比期望的要小。

    JMX

    从Java 5.0开始,类库和JVM提供了全面的管理和监视基础结构JMX。 JMX是用于提供可远程访问的管理界面的标准化方法,并且是向应用程序添加灵活而强大的管理界面的简便方法。 JMX组件(称为受管Bean(MBean))是JavaBean,它们提供与实体管理有关的访问器和业务方法。 每个受管实体(可以是整个应用程序或应用程序内的服务)实例化MBean并使用易于理解的名称进行注册。 启用JMX的应用程序依赖于MBeanServer ,该MBeanServer充当MBean的容器,提供远程访问,名称空间管理和安全服务。 在客户端, jconsole工具可以充当通用JMX客户端。 综上所述,对JMX的平台支持大大减少了应用程序支持外部管理界面所需的工作量。

    除了提供MBeanServer实现之外,Java SE 5.0还对JVM进行了检测,以提供对内存管理,类加载,活动线程,日志记录和平台配置状态的轻松可见性。 默认情况下,大多数平台服务的监视和管理功能都是打开的(对性能的影响很小),因此只需使用JMX客户端连接到应用程序即可。 图1显示了jconsole JMX客户端(JDK的一部分),其中显示了一个内存管理视图-随时间推移的堆使用情况。 “执行GC”按钮说明JMX除了查看操作统计信息之外,还提供了启动操作的功能。

    图1.使用jconsole查看堆使用情况

    运输与安全

    JMX指定用于在MBeanServer和JMX客户端之间进行通信的协议,该协议可以在各种传输方式上运行。 提供了用于本地连接,RMI和SSL的内置传输,并且可以通过JMX连接器API创建新的传输。 身份验证由传输强制执行; 本地传输允许您以相同的用户ID连接到在本地系统上运行的JVM,远程传输可以使用密码或证书进行身份验证。 默认情况下,在Java 6下启用了本地传输。 要在Java 5.0下启用它,您需要在启动JVM时定义系统属性com.sun.management.jmxremote 。 文档“使用JMX监视和管理”(请参阅​​参考资料)描述了启用和配置传输的配置步骤。

    检测Web服务器

    检测应用程序以使用JMX很容易。 像许多其他远程调用框架(例如RMI,EJB和JAX-RPC)一样,JMX是基于接口的。 要创建托管服务,您需要创建一个指定管理方法的MBean接口。 然后,您可以创建一个实现该接口的MBean,将其实例化,然后将其注册到MBeanServer 。

    清单1显示了用于Web服务(例如Web服务器)的MBean接口。 它提供获取程序以获取配置信息(例如端口号)和操作信息(例如服务是否启动)。 它还包含用于查看和更改可配置参数(例如当前日志记录级别)的getter和setter以及用于调用管理操作(例如start()和stop() 。

    清单1. Web服务器的MBean接口
    public interface WebServerMBean { public int getPort(); public String getLogLevel(); public void setLogLevel(String level); public boolean isStarted(); public void stop(); public void start(); }

    通常,实现MBean类非常简单,因为MBean接口应该反映现有实体或服务的属性和管理操作。 例如,MBean中的getLogLevel()和setLogLevel()方法将简单地转发到Web服务器正在使用的Logger上的getLevel()和setLevel()方法。 JMX施加了一些命名限制; 例如,MBean接口名称必须以MBean结尾,并且FooMBean接口的MBean类必须称为Foo 。 (您可以使用更高级的JMX功能动态MBean来解除此限制。)使用默认的MBeanServer注册MBean也很容易,如清单2所示:

    清单2.使用内置的JMX实现注册MBean
    public class WebServer implements WebServerMBean { ... } ... WebServer ws = new WebServer(...); MBeanServer server = ManagementFactory.getPlatformMBeanServer(); server.registerMBean(ws, new ObjectName("myapp:type=webserver,name=Port 8080"));

    传递给registerMBean()的ObjectName标识受管实体。 因为预计给定的应用程序可能包含许多托管实体,所以该名称包含一个域(清单2中的“ myapp”)以及标识该域内托管资源的多个键值对。 密钥“名称”和“类型”是常用的,并且当存在时,名称应在该域内的该类型的所有MBean中唯一地标识受管实体。 还可以指定其他键值对,并且JMX API包括用于通配符匹配对象名称的功能。

    创建并注册了MBean之后,您可以立即将jconsole指向应用程序(在命令行中键入jconsole ),并在“ MBeans”视图中查看其管理属性和操作。 图2显示了新MBean的jconsole中的Attributes选项卡,图3显示了Operations选项卡。 通过反射,JMX可以确定哪些属性是只读的( Started , Port )和哪些属性是可读写的( LogLevel ),并且jconsole允许您修改读写属性。 如果读写属性的设置器抛出异常(例如IllegalArgumentException ),则JMX会将异常报告回客户端。

    图2. jconsole中MBean的“属性”选项卡
    图3. jconsole中用于MBean的“操作”选项卡

    资料类型

    MBean中的访问器和操作可以在其签名中使用任何原始类型,以及String , Date和其他标准库类。 您还可以使用这些允许的类型的数组和集合。 MBean方法也可以使用其他可序列化的数据类型,但是这样做可能会引起互操作性问题,因为类文件也必须对JMX客户端可用。 (如果您使用的是RMI传输,则可以使用RMI的自动类下载功能将所需的类传递给客户端。)如果您希望在管理界面中使用结构化数据类型,同时避免与类可用性相关的互操作性问题,您可以使用JMX的Open MBeans功能表示复合数据或表格数据。

    检测服务器应用程序

    在创建管理界面时,某些参数和操作会建议自己作为显而易见的候选对象,例如配置参数,操作统计信息,调试操作(例如更改日志记录级别或将应用程序状态转储到文件中)以及生命周期操作(启动, 停)。 改造应用程序以支持对这些属性和操作的访问通常很容易。 但是,为了从JMX中获得最大价值,您可能需要在设计时考虑在运行时哪些类型的数据对用户和操作员有用。

    如果要使用JMX深入了解服务器应用程序的工作状况,则需要一种识别和跟踪工作单元的方法。 如果使用标准的Runnable和Callable接口来描述任务,则通过使任务类自描述(例如,通过实现明智的toString()方法),可以跟踪任务在其生命周期中进行的过程,并提供MBean方法来返回等待,处理中和已完成任务的列表。

    清单3中的TrackingThreadPool演示了ThreadPoolExecutor的子类,该子类跟踪正在进行的任务以及已完成任务的计时统计信息。 它通过重写beforeExecute()和afterExecute()挂钩并提供获取方法来检索所收集的数据来完成这些任务。

    清单3.收集正在进行的任务和平均任务时间统计信息的线程池类
    public class TrackingThreadPool extends ThreadPoolExecutor { private final Map<Runnable, Boolean> inProgress = new ConcurrentHashMap<Runnable,Boolean>(); private final ThreadLocal<Long> startTime = new ThreadLocal<Long>(); private long totalTime; private int totalTasks; public TrackingThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); inProgress.put(r, Boolean.TRUE); startTime.set(new Long(System.currentTimeMillis())); } protected void afterExecute(Runnable r, Throwable t) { long time = System.currentTimeMillis() - startTime.get().longValue(); synchronized (this) { totalTime += time; ++totalTasks; } inProgress.remove(r); super.afterExecute(r, t); } public Set<Runnable> getInProgressTasks() { return Collections.unmodifiableSet(inProgress.keySet()); } public synchronized int getTotalTasks() { return totalTasks; } public synchronized double getAverageTaskTime() { return (totalTasks == 0) ? 0 : totalTime / totalTasks; } }

    清单4中的ThreadPoolStatusMBean显示了TrackingThreadPool的MBean接口,其中提供了活动任务,活动线程,已完成任务,等待任务的计数,以及当前正在等待执行和当前正在执行的任务的列表。 通过在管理界面中包含等待和执行任务的列表,您不仅可以查看应用程序的工作量,还可以查看应用程序当前的工作情况。 此功能不仅可以让您深入了解应用程序的行为,还可以了解应用程序正在处理的数据集的性质。

    清单4. TrackingThreadPool的MBean接口
    public interface ThreadPoolStatusMBean { public int getActiveThreads(); public int getActiveTasks(); public int getTotalTasks(); public int getQueuedTasks(); public double getAverageTaskTime(); public String[] getActiveTaskNames(); public String[] getQueuedTaskNames(); }

    如果您的任务足够繁重,则甚至可以更进一步,并在提交任务时为每个任务注册一个MBean(并在完成任务后注销它)。 然后,您可以使用管理界面查询每个任务的执行情况,执行时间或请求取消该任务。

    ThreadPoolStatus清单5个实现了在ThreadPoolStatusMBean接口,对于每个访问器提供明显的实现。 与MBean实现类的典型情况一样,每个操作的实现都很简单,委托给基础托管对象。 在此示例中,JMX代码与托管实体的代码完全分开。 TrackingThreadPool对JMX一无所知。 它通过提供相关属性的管理方法和访问器来提供自己的程序化管理界面。 您可以选择直接在实现类中实现管理功能(使用TrackingThreadPool实现TrackingThreadPoolMBean接口),也可以选择单独实现(如清单4和5所示)。

    清单5. TrackingThreadpool的MBean实现
    public class ThreadPoolStatus implements ThreadPoolStatusMBean { private final TrackingThreadPool pool; public ThreadPoolStatus(TrackingThreadPool pool) { this.pool = pool; } public int getActiveThreads() { return pool.getPoolSize(); } public int getActiveTasks() { return pool.getActiveCount(); } public int getTotalTasks() { return pool.getTotalTasks(); } public int getQueuedTasks() { return pool.getQueue().size(); } public double getAverageTaskTime() { return pool.getAverageTaskTime(); } public String[] getActiveTaskNames() { return toStringArray(pool.getInProgressTasks()); } public String[] getQueuedTaskNames() { return toStringArray(pool.getQueue()); } private String[] toStringArray(Collection<Runnable> collection) { ArrayList<String> list = new ArrayList<String>(); for (Runnable r : collection) list.add(r.toString()); return list.toArray(new String[0]); } }

    为了说明这些类如何提供应用程序正在工作的可视性,请考虑将Web爬网程序将其工作分为两种任务:获取远程页面和为页面建立索引。 每个任务都由FetchTask或IndexTask描述,如清单6所示。您可以创建ThreadPoolStatus MBean来为用于处理这些任务的线程池提供管理接口,并将其注册到JMX。

    清单6. Web爬网程序应用程序中使用的FetchTask类
    public class FetchTask implements Runnable { private final String name; public FetchTask(String name) { this.name = name; } public String toString() { return "FetchTask: " + name; } public void run() { /* Fetch remote resource */ } }

    随着搜寻器对每个页面进行处理,新任务可能会排队等待提取与该页面链接的页面,因此在任何给定时间,都有可能存在未完成的提取任务和索引任务的混合。 能够准确地识别正在处理或等待处理的页面,不仅使您能够了解应用程序的性能特征,而且还可以了解其运行的数据的特征。

    图4显示了正在处理whitehouse.gov网站的Web爬网程序的快照。 您可以看到主页已经被获取并建立索引,并且搜寻器正在获取和索引直接与其链接的页面。 通过单击“ 刷新”按钮,您可以采样通过应用程序的任务流,这可以提供有关应用程序工作方式的大量信息,而无需引入大量的日志记录或在调试器中运行它。

    图4. Web爬网程序中的活动任务和排队任务

    摘要

    平台中的JMX支持和jconsole JMX客户端的结合提供了一种轻松的方法来为我们的应用程序添加管理和监视功能。 即使对于没有特定管理要求的应用程序,通过内置这些功能,您也可以毫不费力地洞悉程序的行为方式以及所处理数据的性质。 如果您的应用程序导出了一个管理界面,使您可以查看其运行情况,则可以了解其运行情况,并更有信心确保它按预期运行,而无需采用诸如添加日志代码之类的侵入性机制。或使用调试器或分析器。


    翻译自: https://www.ibm.com/developerworks/java/library/j-jtp09196/index.html

    相关资源:JMX一步一步来,快速学会开发JMX应用
    Processed: 0.021, SQL: 9