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

在继续分析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(){

			@Override
			public void handleNotification(Notification notification,
					Object handback) {
				// TODO Auto-generated method stub
				System.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上面去。。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值