Tomcat源码阅读之JMX部分的设计与实现

来源:互联网 发布:可编程控制器软件 编辑:程序博客网 时间:2024/04/28 04:16

在继续分析Tomcat的源码之前,先来分析一下Tomcat对JMX的支持,以及在Tomcat中的一些代码实现细节。。。

这里来看看Tomcat都通过JMX托管了那些对象吧:



嗯,从图上就可以看出,托管的东西还挺多的。。。其实感觉通过JMX这种方式还挺高端的,起码可以实现远程对Tomcat服务器的管理和配置。。。还挺方便的。。。

好啦,接下来就来看看Tomcat中是如何通过JMX来托管自己的对象的吧。。。

先来看一张比较重要的图:



这里是Tomcat中JMX部分比较重要的几个基本类型吧,首先是BaseModelMBean类型:

首先它实现了DynamicMBean接口,嗯,这个接口是啥呢?如果对JMX有稍微多一些了解的话,就会知道了,实现了这个接口,代表这个类型的对象可以被注册到MBServer上面去,这与前面的自己的一一篇文章里面介绍的注册到MBServer上面的对象少有不懂,Dynamic意味着这个对象是动态的,JMX通过调用一些方法可以动态的来获取这个对象的信息,它的属性,它暴露的方法什么的,以及进行动态的调用。。。这里具体的内容就不详细说了,去稍微了解一下JMX的部分就很容易明白了。。


另外它还是先了ModelMBeanNotificationBroadcaster,代表当前对象可以作为NotificationListener的容器,可以在这个对象里面添加listener,当当前托管的对象的属性发生改变了之后会通知这些注册的listener进行一些处理,例如写log啥的。。。


好啦,介绍完了BaseModelMBean类型,接下来来看看ManagedBean类型吧,它是干嘛的呢,刚开始这名字把我给糊弄了。。。。在介绍它之前先来看一段XML配置文件:

<mbeans-descriptors><mbean name="StandardServer" description="Standard Server Component" domain="Catalina" group="Server" type="org.apache.catalina.core.StandardServer"><attribute name="address" description="The address on which we wait for shutdown commands." type="java.lang.String"/><attribute name="managedResource" description="The managed resource this MBean is associated with" type="java.lang.Object"/><attribute name="port" description="TCP port for shutdown messages" type="int"/><attribute name="serverInfo" description="Tomcat server release identifier" type="java.lang.String" writeable="false"/><attribute name="serviceNames" description="Object names of all services we know about" type="[Ljavax.management.ObjectName;" writeable="false"/><attribute name="shutdown" description="Shutdown password" type="java.lang.String"/><attribute name="stateName" description="The name of the LifecycleState that this component is currently in" type="java.lang.String" writeable="false"/><operation name="await" description="Wait for the shutdown message" impact="ACTION" returnType="void"/><operation name="storeConfig" description="Save current state to server.xml file" impact="ACTION" returnType="void"></operation></mbean></mbeans-descriptors>

这个是在Tomcat代码中,core包里面的mbeans-descriptors.xml里面的内容,看内容就知道这个是对StanderServer对象的描述,例如属性,方法啥的。。。

那么这里就可以开始说ManagedBean是干啥的了,这里,一个<mbean></mbean>元素就会对应生成一个ManagedBean对象,用于保存和记录XML关于对象的描述。。。


好啦,到这里如果有一定基础的话就已经知道ManagedBean与BaseModelMBean之间的关系了,以及是如何运行的了。。

上图中还有一个MBean对象。。。那么它是干啥的呢。。?就拿StanderServer来举例子吧,

(1)首先Tomcat会通过读取刚刚说的xml文件,这些文件分散在Tomcat的许多源码包中

(2)通过里面元素的声明,创建相应的ManagedBean对象,用于维护这些信息。。

(3)当创建了一个StanderServer对象之后,会先找到它对应的ManagedBean对象,然后用这两个对象来生成相应的BaseModelMBean对象,然后将这个对象注册到MBServer上面去。。。

这样也就动态的将StanderServer通过XML文件的描述注册到了JMX上去,生成的BaseModelMBean对象通过调用StanderServer的ManagedBean对象,将需要的数据暴露给JMX就可以了。。。。

如下图:



可以看到这里暴露出来的属性以及方法与XML文件里面描述的是相同的。。。


这样一来,就算是基本上搞清楚了Tomcat如何通过xml文件来动态的生成对一些对象在JMX的动态注册。。


好了,有了上面的内容,接下来再来看看Tomcat中如何具体使用这些类型,这里就有两个重要的类型要介绍了:

首先是MBeanUtils类型,它是一个工具类,主要是进行一些初始化。。。。

我们来看他的两个重要的类属性:

    /**     * The configuration information registry for our managed beans.     */    private static Registry registry = createRegistry();  //创建registry对象    /**     * The <code>MBeanServer</code> for this application.     */    private static MBeanServer mserver = createServer();  //创建mbeanserver

这里首先创建了Registry对象,接着创建了MBeanServer对象,先来看看createRegistry方法吧:

    public static synchronized Registry createRegistry() {        if (registry == null) {            registry = Registry.getRegistry(null, null);   //这里会创建Registry对象            ClassLoader cl = MBeanUtils.class.getClassLoader();            //用当前的应用程序classLoader来家在这些包里面的mbeans xml,并且用Registry对象来加载这些文件里面定义的mbean,生成ManagedBean            //这里managedbean的作用是保存管理那些mbean的描述信息,待会用这些信息来构造属于这个mbean的DynamicMBean,然后保存在rregistry对象里面            registry.loadDescriptors("org.apache.catalina.mbeans",  cl);            registry.loadDescriptors("org.apache.catalina.authenticator", cl);            registry.loadDescriptors("org.apache.catalina.core", cl);            registry.loadDescriptors("org.apache.catalina", cl);            registry.loadDescriptors("org.apache.catalina.deploy", cl);            registry.loadDescriptors("org.apache.catalina.loader", cl);            registry.loadDescriptors("org.apache.catalina.realm", cl);            registry.loadDescriptors("org.apache.catalina.session", cl);            registry.loadDescriptors("org.apache.catalina.startup", cl);            registry.loadDescriptors("org.apache.catalina.users", cl);            registry.loadDescriptors("org.apache.catalina.ha", cl);            registry.loadDescriptors("org.apache.catalina.connector", cl);            registry.loadDescriptors("org.apache.catalina.valves",  cl);            registry.loadDescriptors("org.apache.catalina.storeconfig",  cl);            registry.loadDescriptors("org.apache.tomcat.util.descriptor.web",  cl);        }        return (registry);    }

这里其实做的事情首先由创建Registry对象,通过Registry类型的静态方法来创建的。。。接着做的事情其实分别加载下面的包里面的mbeans-descriptors.xml文件,然后创建相应的ManagedBean对象。。。。

    public static synchronized MBeanServer createServer() {        if (mserver == null) {            mserver = Registry.getRegistry(null, null).getMBeanServer();  //从registry对象中获取mbserver对象        }        return (mserver);    }


这里创建mbserver其实也是调用的Registry的静态方法来做的。。。

好了。。对于MBeanUtils类型,就先介绍到这里吧。。。

接下来来看看Registry的定义:

    private MBeanServer server = null;  //用于注册的mbserver    /**     * The set of ManagedBean instances for the beans this registry     * knows about, keyed by name.     */    private HashMap<String,ManagedBean> descriptors = new HashMap<>();  //key是定义的mbean的名字,value是生成的ManagedBean对象,他保存的有mbean的具体信息    /** List of managed beans, keyed by class name     */    private HashMap<String,ManagedBean> descriptorsByClass = new HashMap<>();  //其实key是className,value是为其生成的ManagedBean(mbean里面定义的type)    // map to avoid duplicated searching or loading descriptors    private HashMap<String,URL> searchedPaths = new HashMap<>();   //用于索引包里面的mbean xml ,key是包的名字

首先是比较重要的属性的定义,这里具体他们是干嘛用的后面的注释应该说的还蛮清楚的。。。

     //用于返回用到的Registry对象    public static synchronized Registry getRegistry(Object key, Object guard) {        Registry localRegistry;        if( perLoaderRegistries!=null ) {            if( key==null )                key=Thread.currentThread().getContextClassLoader();            if( key != null ) {                localRegistry = perLoaderRegistries.get(key);                if( localRegistry == null ) {                    localRegistry=new Registry();//                    localRegistry.key=key;                    localRegistry.guard=guard;                    perLoaderRegistries.put( key, localRegistry );                    return localRegistry;                }                if( localRegistry.guard != null &&                        localRegistry.guard != guard ) {                    return null; // XXX Should I throw a permission ex ?                }                return localRegistry;            }        }        // static        if (registry == null) {            registry = new Registry();        }        if( registry.guard != null &&                registry.guard != guard ) {            return null;        }        return (registry);    }

getRegistry方法,可以看到这里其实用了一个单例的Registry对象,

接下来我们来看一下最重要的Registry的registerComponent方法吧,它用于在mbserver上面注册对象:

    //注册一个组件    public void registerComponent(Object bean, ObjectName oname, String type)           throws Exception    {        if( log.isDebugEnabled() ) {            log.debug( "Managed= "+ oname);        }        if( bean ==null ) {            log.error("Null component " + oname );            return;        }        try {            if( type==null ) {  //如果没有名字,那么获取类型的名字                type=bean.getClass().getName();            }            ManagedBean managed = findManagedBean(null, bean.getClass(), type);  //获取这个对象所用的ManagedBean            // The real mbean is created and registered            DynamicMBean mbean = managed.createMBean(bean);  //通过ManagedBean创建一个DynamicMBean            if(  getMBeanServer().isRegistered( oname )) {                if( log.isDebugEnabled()) {                    log.debug("Unregistering existing component " + oname );                }                getMBeanServer().unregisterMBean( oname );  //如果这个已经注册了,那么取消这个名字的注册            }            getMBeanServer().registerMBean( mbean, oname);  //将刚刚创建的ManagedBean注册起来        } catch( Exception ex) {            log.error("Error registering " + oname, ex );            throw ex;        }    }

其实这里跟上面分析的内容是一直的,首先获取要注册对象的ManagedBean对象,然后通过它来创建一个DynamicMBean对象,这里一般都是BaseModelMBean类型的对象(有的时候可以通过在xml文件中指定,为对象创建其他的类型),然后在将刚刚创建的DynamicMBean对象注册到mbserver上面去。。。。


好啦,总的来说Tomcat这部分做的还挺好的,通过XML文件描述来动态的在mbserver上面注册对象。。。

而且可以很方便的使用。。如下代码,利用tomcat的代码来动态的注册自己定义的对象:

package registTest;import javax.management.Notification;import javax.management.NotificationListener;import javax.management.ObjectName;import org.apache.tomcat.util.modeler.BaseModelMBean;import org.apache.tomcat.util.modeler.ManagedBean;import org.apache.tomcat.util.modeler.Registry;public class Fjs {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public void hello() {System.out.println("hello  : " + name);}public static void main(String args[]) throws Exception {Registry registry = new Registry();registry.loadDescriptors("registTest",  Fjs.class.getClassLoader());ObjectName name = new ObjectName("fjs:type=hello");Fjs fjs = new Fjs();ManagedBean manager = registry.findManagedBean(Fjs.class.getName());BaseModelMBean dmb = (BaseModelMBean)manager.createMBean(fjs);dmb.addAttributeChangeNotificationListener(new NotificationListener(){@Overridepublic void handleNotification(Notification notification,Object handback) {// TODO Auto-generated method stubSystem.out.println(notification);}}, null, null);registry.getMBeanServer().registerMBean(dmb, name);Thread.currentThread().sleep(Long.MAX_VALUE);}}

嗯,Fjs类型的描述文件如下:

<mbeans-descriptors><mbean name="Fjs" description="Standard Server Component" domain="Catalina" group="Server" type="registTest.Fjs"><attribute name="name" description="名字" type="java.lang.String"/><operation name="hello" description="Wait for the shutdown message" impact="ACTION" returnType="void"/></mbean></mbeans-descriptors>

简单吧。。没做多少工作就实现了通过xml文件描述动态的注册对象到mbserver上面去。。。。

0 1