今天我研究了一下tomcat的jmx实现,小做总结,以期在总结过程能有更深刻的理解。
不了解jmx的可以下百度下jmx,网上有很多介绍jmx的文章。我在这里就不再赘述了。这篇文章主要总结tomcat基于jmx的实现。
首先来看下tomcat实现jmx后什么效果。那么来启用jmx吧。在tomcat启动参数里加入-Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false 参数,启用jmx并指定jmx端口为8999。如图:
然后启动tomcat。打开jconsole.exe,它在jdk安装目录的bin目录下。在远程进程下输入 你的电脑ip:8999 然后连接。
连接上如下图所示
有7和选项卡,前6个选项卡中的内容都是从从第7个选项卡中比较重要的内容进行图形化显示。所以我们直接看第7个选项卡MBean中的内容,这个选项卡中列举了所有可管理的MBean,我挑一个熟悉的来看下神奇的JMX能做什么。我找到了Manager/localhost下的一个叫做manager的MBean,manager是我们熟悉的tomcat的session管理器。我就用它来深入研究tomcat中jmx的设计和实现吧。其中属性就是这个MBean暴露的可管理的属性,操作是MBean暴露的可操作的方法。
我们看到manager有个叫activeSessions的属性。看名称应该指的是目前manager里存活的session数。
我们可以查看现在manager中session的存活数。现在是一个,我们看到这个属性值的可读是true,可写入是false的,说明这个属性可读但不可写(后面我们再来看这个属性为什么可读不可写,而有的属性是可读也可写的)。下面我们看看操作,我点一下listSessionIds方法,顾名思义这个方法应该是返回所有的sessionId的,然后我们就神奇的拿到了刚才所说的存活的sessionId(如果测试的时候没有sessionId,大家可以访问下tomcat的管理页面:http://localhost:8080/manager/html,tomcat就会产生一个sessionId)。复制一下这个sessionId,然后看下expireSession方法。
这个方法顾名思义就是让一个sessionId过期的方法,这个方法需要一个sessionId作为参数,我们把刚才复制的那个sessionId粘贴进行。然后点击执行expireSession方法。界面显示已调用成功该方法。
调用成功后我们再来看下那个activeSessions属性和再调用下listessionds方法。
我们看到activeSessions的值为0,listessionds方法并未返回任何sessionId说明我们刚才调用的expireSession方法把那个唯一存活的sessionId设置为过期了。这样存活id数就成了0,listSessionIds也无法返回sessionId了。
到这里jmx的基本功能就了解了吧。通过JMX,我们可以在程序运行中管理一些对象(这些对象通常是资源对象),属性或方法。JMX由三部分组成,1、资源植入层 ,也就是我们自己实现的要暴露出来可以管理的Mbean。2、代理层,就是管理Mbean的组件。3、服务层,就是对外提供统一管理接口的服务,我们的jconsole就是通过这个服务层接口来管理Mbean的。
简单说下java jmx框架。
JMX(Java Management Exrensions)技术是标准Java平台的一部分。JMX技术从J2SE 5.0发布的时候添加到Java2平台。JMX提供了一个标准的方法去管理资源,例如:应用,设备和服务。因为JMX是一种动态技术,你可以在被管理资源创建、实例化和实现的时候监控和管理他们。你也可以使用JMX技术去监听和管理Java虚拟机。JMX规范为Java语言定义了监听应用程序和网络所需要的架构、设计模式,API和服务。在JMX中,一个给定的资源被描述成一个或多个被管理的Bean(Managed Beans 或者 MBeans)。这些MBeans被注册到一个中心管理对象服务器,叫做MBean Server。MBean Server作为一个管理代理可以在几乎所有的支持Java的设备上。规范定义了JMX代理,你可以使用它管理所有已经被正确配置的资源。一个JMX代理包括一个MBean Server(注册MBean的地方)和一系列处理MBean的服务。使用这种方法可以让远程的管理系统通过JMX代理可以直接控制资源。因为资源管理的方式和管理的基础设施完全独立,因此资源可以被管理,而不用关心他们是如何实现的。JMX定义了标准的连接器(JMX连接器)去确保你可以通过远程管理应用访问JMX代理。JMX连接器为同样的管理接口提供了不同的协议。因此,管理应用程序可以不管使用的连接协议透明的管理资源。当然了JMX代理也可以用于不符合JMX规范的系统或者应用,只要这些系统或者应用支持JMX代理。
JMX标准提供了四种不同的 MBean:
Standard MBean 直接实现用于管理对象的方法,既可以通过实现一个由程序员定义的、类名以 “MBean” 结束的接口,也可以使用一个以一个类作为构造函数参数的 Standard MBean 实例,加上一个可选的接口类规范。这个接口可以开放用于管理的部分对象方法。
Dynamic MBean 用属性访问器动态地访问属性,并用一个一般化的 invoke() 方法调用方法。可用的方法是在 MBeanInfo 接口中指定的。这种方式更灵活,但是不具有像 Standard MBean 那样的类型安全性。它极大地降低了耦合性,可管理的 POJO(纯粹的老式 Java 对象)不需要实现特定的接口。
Model MBean 提供了一个改进的抽象层,并扩展了 Dynamic MBean 模型以进一步减少对给定实现的依赖性。这对于可能使用多个版本的 JVM 或者需要用松散耦合管理第三方类的情况会有帮助。Dynamic MBean 与 Model MBean 之间的主要区别是,在 Model MBean 中有额外的元数据。
Open MBean 是受限的 Model MBean,它限制类型为固定的一组类型,以得到最大的可移植性。通过限制数据类型,可以使用更多的适配器,并且像 SMTP 这样的技术可以更容易适应 Java 应用程序的管理。这种变体还指定了数组和表等标准结构以改进复合对象的管理。
在Tomcat9动态MBean(Dynamic MBean)的使用,本文就介绍这种MBean。
要实现动态Mbean就需要实现javax.management.DynamicMBean接口,实现他的以下6个方法。
但是tomcat的MBean很多,如果每个都要实现DynamicMBean的6个方法也是很烦,所以tomcat就采用通过配置文件(即mbeans-descriptors.xml)结合通用的动态MBean(org.apache.tomcat.util.modeler.BaseModelMBean)、描述MBean配置信息的org.apache.tomcat.util.modeler.ManagedBean来简化MBean的构造。我们来看看管理session的Manager的Mbean是如何生成的。
Mbean的生成入口在GlobalResourcesLifecycleListener,它是在Sserver.xml中配置的监听器。看下图:
在这个监听器中有个静态变量
我们知道静态变量是在加载类的时候就初始化的。继续看下MBeanUtils.createRegistry()方法。
继续看下loadDesriptors()方法。
这个方法我们看到他构建了一个指向程序包下mbeans-descriptors.xml的url,并传入了load方法。继续看下load方法。
public List load( String sourceType, Object source, String param) throws Exception { if( log.isTraceEnabled()) { log.trace("load " + source ); } String location=null; String type=null; Object inputsource=null; if( source instanceof URL ) { URL url=(URL)source; location=url.toString(); type=param; inputsource=url.openStream(); if (sourceType == null && location.endsWith(".xml")) { sourceType = "MbeansDescriptorsDigesterSource"; } } else if( source instanceof File ) { location=((File)source).getAbsolutePath(); inputsource=new FileInputStream((File)source); type=param; if (sourceType == null && location.endsWith(".xml")) { sourceType = "MbeansDescriptorsDigesterSource"; } } else if( source instanceof InputStream ) { type=param; inputsource=source; } else if( source instanceof Class ) { location=((Class)source).getName(); type=param; inputsource=source; if( sourceType== null ) { sourceType="MbeansDescriptorsIntrospectionSource"; } } if( sourceType==null ) { sourceType="MbeansDescriptorsDigesterSource"; ModelerSource ds=getModelerSource(sourceType); List mbeans = ds.loadDescriptors(this, type, inputsource); return mbeans; }
继续看下loadDescriptors方法
直接看excute方法()。
这段代码是不是似曾相识,tomcat在生成server和各层容器时也用的Digester类。它将xml文件解析成对象。我们看下它的解析规则,进入createDigester方法。
简单说下,这个方法里是设置解析规则的。第56到58行说的是在解析xml中遇到mbeans-descriptors节点下的mbean节点就生成一个org.apache.tomcat.util.modeler.ManagedBean对象,并放入栈顶。第59到60行说的是遇到mbeans-descriptors/mbean节点按照属性值调用栈顶对象的set方法设置属性。第61到64行说的是遇到mbeans-descriptors/mbean节点调用次栈顶的add方法传入栈顶元素。
理解了解析规则,继续看下mbeans-descriptors.xml文件吧。
所以最终Digester把xml文件解析成org.apache.tomcat.util.modeler.ManagedBean对象了。最终得到一个ManagedBean列表,是解析该目录下所有需要生成MBean的ManagedBean对象。
然后ManagedBean怎么最终成为Mbean的。直接说结果,因为各个对象生成Mbean入口不一样,但是最终都调用ManagedBean的createMBean方法,我们看下该方法。
我们先不看else里的内容,看其中简单的一种构造方式,首先new了一个BaseModelMbean对象,然后把自身添加进BaseModelMbean对象。BaseModelMbean实现了动态Mbean接口!!!
然后BaseModelMbean通过接口方法把ManagedMbean中记录的可操作属性和方法暴露给MbeanServer。真是一个复杂的工程。看完累劈了!!!