Mbean
名词解释
Mbean
managed beans 被管理的bean
描述一个可管理的资源。
是一个Java对象,遵循以下一些语义:
java对象的属性(property)叫属性(attribute),方法(method)叫操作(operations)。- Mbean的定义:
必须:是公用的,非抽象的类
必须:有至少一个公用的构造器
必须:实现它自己的相应的MBean接口或者实现 javax.management.DynamicMBean 接口
可选:一个MBean可以实现 javax.management.NotificationBroadcaster 接口 - Mbean的类型
标准MBean
动态MBean
模型MBean
开放MBean Mbean的使用步骤
1)注册Mbean
一个MBeanServer的主要职责是在一个JMX代理中维护一个MBean的注册表。
MBean能够以下面两个方法中的任意一个注册:
<1>创建一个MBean实例并注册它到MBeanServer用方法:public ObjectInstance registerMBean(Object object, ObjectName name)
这里,object是创建的MBean实例,name部分是MBean的一个唯一标志符。
<2>使用createMBean 方法中的一个:public ObjectInstance createMBean(String classname, ObjectName name) public ObjectInstance createMBean(String classname, ObjectName name, ObjectName loader) public ObjectInstance createMBean(String classname, ObjectName name, Object[] params, String[] types) public ObjectInstance createMBean(String classname, ObjectName name, ObjectName loader, Object[] params, String[] types)
createMBean方法使用java自省来创建一个MBean实例。对所有的createMBean方法,有两个变量是通用的:
String classname – 要创建的MBean的实例的类名。
ObjectName name – MBean要注册的对象名称。
如果用两个变量的createMBean构造器,缺省的类装载器(class loader)用来装载MBean类,MBean被使用缺省的构造器初始化。如果MBean已经被初始化,这个实例将被MBeanServer用第二个变量的对象名称注册。如果你想使用指定的类装载器,那么可以使用三个变量的构造器。这个类装载器将被MBeanServer注册为一个MBean。这个类装载器的对象名称将用做第三个变量。
当实例化一个类,如果任何参数必须传入,那么可以使用四个参数的createMBean方法。这个createMBean方法的最后的两个参数分别包含对象数组(类的初始化必须的)和他们的署名。如果MBean类必须用其他指定的类装载器装载,那么应该使用五个参数的构造器。
2)注册一个MBean的多个实例
你可能需要监控十多个Web 服务器。如果所有的web服务器的管理信息是相同的,那么你不必编写十个MBean。一个MBean足够了。我们能够创建多个实例并注册每一个实例以一个唯一的对象名称。
现在可以看到使用对象名称来避免混淆的重要性。
很明显服务器的端口数字和服务器的名称将是任意服务器的唯一,那是因为,在一个机器上,你不能运行两个服务器在一个端口上。
于是,可以创建对象名称如
Servers:hostName=localhost,portNumber=xxxx
假设超过一个服务器在同一个机器上启动,那么对象名称应该为
Servers:hostName=localhost,portNumber=yyyy
由于对象名称是注册MBean的唯一标志,多个MBean的实例将被注册为不同的对象名称。
对象名称通常用他们包含的关键值对来区分,如一个服务器MBean的主机名和端口名关键字及他们各自的值。同理,唯一标志相似的MBean组,对象名称将用相应的关键字和值对拼装。例如,type=ServerMBean指出这个MBean是一个服务器MBean类型。由此,对象名称值Server:type=ServerMBean,hostName=localhost,portNumber=8050 提供一个简单的MBean分组,产生为服务器MBean的实现。并且也提供了一个方法来唯一标志在localhost:8050上运行的服务器的MBean实例。
一个简单的示范
让提供管理信息(所有web服务器通用的)的Mbean为动态MBean,并且起名为ServerInfo。
让两个web服务器运行在机器A(machineA)的端口8080和80。让一个web服务器运行在机器B(machineB)的8080端口,另一个web服务器运行在机器C(machineC)的80端口。总之,我们需要管理这四个web服务器。我们能够用ServerInfo 类和创建四个实例来管理每一个web服务器。注册四个MBean的代码片断如下:ServerInfo srvr1 = new ServerInfo(); ServerInfo srvr2 = new ServerInfo(); ServerInfo srvr3 = new ServerInfo(); ServerInfo srvr4 = new ServerInfo(); MBeanServer mbs = null; try { mbs = MBeanServerFactory.createMBeanServer( ); mbs.registerMBean(srvr1, new ObjectName("Server:hostName=machineA,portNumber=8080")); mbs.registerMBean(srvr2, new ObjectName("Server:hostName=machineA,portNumber=80")); mbs.registerMBean(srvr3, new ObjectName("Server:hostName=machineB,portNumber=8080")); mbs.registerMBean(srvr4, new ObjectName("Server:hostName=machineC,portNumber=80")); } catch (Exception e) { e.printStackTrace(); }
3)注销MBean
MBean能够用MBeanServer的下面方法注销:public void unregisterMBean(ObjectName name)
这里name是这个MBean实例注册的对象名称。
注销MBean后,MBeanServer将不再保存任何这个MBean实例的关联。
控制MBeanRegistration
JMX定义了一个接口叫MBeanRegistration。这个接口的目的是允许MBean开发者对在MBeanServer上注册和注销进行一些控制。这通过MBean实现javax.management.MBeanRegistration接口来达到。
MBeanRegistration接口定义注册控制机制的行为。它定义了以下四个方法:public ObjectName preRegister(MBeanServer mbs, ObjectName name) public void postRegister() public void preDeRegister() public void postDeRegister()
所有以上的方法都是回调方法,MBeanServer将在恰当的时机调用这些方法。
如果一个MBean实现MBeanRegistration并且这个MBean被注册,MBeanServer在注册前调用preRegister方法。这个方法返回的对象名称将在MBean注册过程中使用。在成功完成MBean注册后,MBeanServer调用postRegister方法。
如果以上的MBean被注销,在注销前MBeanServer调用preDeRegister方法。如果注销成功,MBeanServer调用postDeRegister方法。
需要记住的几点
对于一个单一MBean类,多个实例能够被创建,并能够被注册为注册时MBean提供的对象名称。
无论何时一个MBean被注册,MBeanServer创建一个类型为 jmx.mbean.created 的消息。MBeanServerDelegate MBean 广播这个消息到所有注册的监听者。
MBeanRegistration接口提供注册和注销过程监控的钩点。
- Mbean的定义:
JMX
- 知识简介
JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
JMX在Java编程语言中定义了应用程序以及网络管理和监控的体系结构、设计模式、应用程序接口以及服务。通常使用JMX来监控系统的运行状态或管理系统的某些方面,比如清空缓存、重新加载配置文件等。
优点是可以非常容易的使应用程序被管理
伸缩性的架构使每个JMX Agent服务可以很容易的放入到Agent中,每个JMX的实现都提供几个核心的Agent服务,你也可以自己编写服务,服务可以很容易的部署,取消部署。
主要作用是提供接口,允许有不同的实现 。 - JMX架构分层
JMX体系结构分为以下四个层次:
设备层
设备层(Instrumentation Level):主要定义了信息模型。在JMX中,各种管理对象以管理构件的形式存在,需要管理时,向MBean服务器进行注册。该层还定义了通知机制以及一些辅助元数据类。
代理层
代理层(Agent Level):主要定义了各种服务以及通信模型。该层的核心是一个MBean服务器,所有的管理构件都需要向它注册,才能被管理。注册在MBean服务器上管理构件并不直接和远程应用程序进行通信,它们通过协议适配器和连接器进行通信。而协议适配器和连接器也以管理构件的形式向MBean服务器注册才能提供相应的服务。
分布服务层
分布服务层(Distributed Service Level):主要定义了能对代理层进行操作的管理接口和构件,这样管理者就可以操作代理。然而,当前的JMX规范并没有给出这一层的具体规范。
附加管理协议API
定义的API主要用来支持当前已经存在的网络管理协议,如SNMP、TMN、CIM/WBEM等。
- 知识简介
Spring中对Mbean的支持
Spring中提供了MbeanExporter,将Spring bean的属性和方法导出为MBean服务器中的JMX属性和操作。通过JMX服务器,JMX管理工具(例如JConsole)可以查看到正在运行的应用程序的内部情况。
Spring的MbeanExporter使用方法如下//args为将要导出的bean对象 @Bean public MbeanExporter mbeanExporter(Object args) { MbeanExporter exporter = new MbeanExporter(); Map<String, Object> beans = new HashMap<>(); beans.put("ObjectName", args); exporter.setBeans(beans); return exporter; }
另外一种XML配置的方法
<bean id="mbeanExporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <util:map> <!-- 这里的value-ref都为之前定义的bean的id --> <entry key="bean:name=requestRule" value-ref="requestRule"/> <entry key="bean:name=responseRule" value-ref="responseRule"/> </util:map> </property> </bean>
对MbeanExporter代码的阅读,本文中只摘取部分说明
MebanExporter.javapublic class MBeanExporter extends MBeanRegistrationSupport implements MBeanExportOperations, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, SmartInitializingSingleton, DisposableBean{ public static final int AUTODETECT_NONE = 0; public static final int AUTODETECT_MBEAN = 1; public static final int AUTODETECT_ASSEMBLER = 2; public static final int AUTODETECT_ALL = 3; private static final String WILDCARD = "*"; private static final String MR_TYPE_OBJECT_REFERENCE = "ObjectReference"; private static final String CONSTANT_PREFIX_AUTODETECT = "AUTODETECT_"; private static final Constants constants = new Constants(MBeanExporter.class); private Map<String, Object> beans; private Integer autodetectMode; private boolean allowEagerInit; private MBeanInfoAssembler assembler; private ObjectNamingStrategy namingStrategy; private boolean ensureUniqueRuntimeObjectNames; private boolean exposeManagedResourceClassLoader; private Set<String> excludedBeans; private MBeanExporterListener[] listeners; private NotificationListenerBean[] notificationListeners; private final Map<NotificationListenerBean, ObjectName[]> registeredNotificationListeners; private ClassLoader beanClassLoader; private ListableBeanFactory beanFactory; ...... ...... //随容器启动而启动 public void afterPropertiesSet() { //获取到MbeanServer if (this.server == null) this.server = JmxUtils.locateMBeanServer(); } public void destroy() { this.logger.info("Unregistering JMX-exposed beans on shutdown"); unregisterNotificationListeners(); unregisterBeans(); } public ObjectName registerManagedResource(Object managedResource) throws MBeanExportException { Assert.notNull(managedResource, "Managed resource must not be null"); try { ObjectName objectName = getObjectName(managedResource, null); if (this.ensureUniqueRuntimeObjectNames) objectName = JmxUtils.appendIdentityToObjectName(objectName, managedResource); } catch (Exception ex) { throw new MBeanExportException("Unable to generate ObjectName for MBean [" + managedResource + "]", ex); } ObjectName objectName; registerManagedResource(managedResource, objectName); return objectName; } public void registerManagedResource(Object managedResource, ObjectName objectName) throws MBeanExportException { Assert.notNull(managedResource, "Managed resource must not be null"); Assert.notNull(objectName, "ObjectName must not be null"); try { if (isMBean(managedResource.getClass())) { doRegister(managedResource, objectName); } else { ModelMBean mbean = createAndConfigureMBean(managedResource, managedResource.getClass().getName()); doRegister(mbean, objectName); injectNotificationPublisherIfNecessary(managedResource, mbean, objectName); } } catch (JMException ex) { throw new UnableToRegisterMBeanException("Unable to register MBean [" + managedResource + "] with object name [" + objectName + "]", ex); } } public void unregisterManagedResource(ObjectName objectName) { Assert.notNull(objectName, "ObjectName must not be null"); doUnregister(objectName); } protected void registerBeans() { if (this.beans == null) { this.beans = new HashMap(); if (this.autodetectMode == null) { this.autodetectMode = Integer.valueOf(3); } } int mode = (this.autodetectMode != null) ? this.autodetectMode.intValue() : 0; if (mode != 0) { if (this.beanFactory == null) { throw new MBeanExportException("Cannot autodetect MBeans if not running in a BeanFactory"); } if ((mode == 1) || (mode == 3)) { this.logger.debug("Autodetecting user-defined JMX MBeans"); autodetectMBeans(); } if ((((mode == 2) || (mode == 3))) && (this.assembler instanceof AutodetectCapableMBeanInfoAssembler)) { autodetectBeans((AutodetectCapableMBeanInfoAssembler)this.assembler); } } if (!(this.beans.isEmpty())) for (Map.Entry entry : this.beans.entrySet()) registerBeanNameOrInstance(entry.getValue(), (String)entry.getKey()); } protected ObjectName registerBeanNameOrInstance(Object mapValue, String beanKey) throws MBeanExportException { try { Object bean; if (mapValue instanceof String) { if (this.beanFactory == null) { throw new MBeanExportException("Cannot resolve bean names if not running in a BeanFactory"); } String beanName = (String)mapValue; if (isBeanDefinitionLazyInit(this.beanFactory, beanName)) { ObjectName objectName = registerLazyInit(beanName, beanKey); replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName); return objectName; } bean = this.beanFactory.getBean(beanName); ObjectName objectName = registerBeanInstance(bean, beanKey); replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName); return objectName; } if (this.beanFactory != null) { Map beansOfSameType = this.beanFactory.getBeansOfType(mapValue.getClass(),false, this.allowEagerInit); for (Map.Entry entry : beansOfSameType.entrySet()) { if (entry.getValue() == mapValue) { String beanName = (String)entry.getKey(); ObjectName objectName = registerBeanInstance(mapValue, beanKey); replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName); return objectName; } } } return registerBeanInstance(mapValue, beanKey); } catch (Exception ex) { throw new UnableToRegisterMBeanException("Unable to register MBean [" + mapValue + "] with key '" + beanKey + "'", ex); } } private ObjectName registerBeanInstance(Object bean, String beanKey) throws JMException { ObjectName objectName = getObjectName(bean, beanKey); Object mbeanToExpose = null; if (isMBean(bean.getClass())) { mbeanToExpose = bean; } else { DynamicMBean adaptedBean = adaptMBeanIfPossible(bean); if (adaptedBean != null) { mbeanToExpose = adaptedBean; } } if (mbeanToExpose != null) { if (this.logger.isInfoEnabled()) { this.logger.info("Located MBean '" + beanKey + "': registering with JMX server as MBean [" + objectName + "]"); } doRegister(mbeanToExpose, objectName); } else { if (this.logger.isInfoEnabled()) { this.logger.info("Located managed bean '" + beanKey + "': registering with JMX server as MBean [" + objectName + "]"); } ModelMBean mbean = createAndConfigureMBean(bean, beanKey); doRegister(mbean, objectName); injectNotificationPublisherIfNecessary(bean, mbean, objectName); } return objectName; } ...... ...... }
MbeanRegistrationSupport.java(部分代码段)
protected void doRegister(Object mbean, ObjectName objectName) throws JMException { ObjectName actualObjectName; synchronized (this.registeredBeans) { ObjectInstance registeredBean = null; try { registeredBean = this.server.registerMBean(mbean, objectName); } catch (InstanceAlreadyExistsException ex) { if (this.registrationPolicy == RegistrationPolicy.IGNORE_EXISTING) { if (this.logger.isDebugEnabled()) { this.logger.debug("Ignoring existing MBean at [" + objectName + "]"); } } else if (this.registrationPolicy == RegistrationPolicy.REPLACE_EXISTING) { try { if (this.logger.isDebugEnabled()) { this.logger.debug("Replacing existing MBean at [" + objectName + "]"); } this.server.unregisterMBean(objectName); registeredBean = this.server.registerMBean(mbean, objectName); } catch (InstanceNotFoundException ex2) { this.logger.error("Unable to replace existing MBean at [" + objectName + "]", ex2); throw ex; } } else { throw ex; } } actualObjectName = (registeredBean != null) ? registeredBean.getObjectName() : null; if (actualObjectName == null) { actualObjectName = objectName; } this.registeredBeans.add(actualObjectName); } onRegister(actualObjectName, mbean); }
研究源码得出:Spring的MbeanExporter中声明了一个beans(类型为map)用来保存将要被导出到Mbean的对象,底层是使用MbeanServer的registerMBean方法实现。
对于Spring中Mbean的一些具体操作,会在后续章节详述。