2011年6月27日 星期一

Spring + JMX

JMX(Java Management Extensions,即Java管理擴展)是Java平台上為應用程序、設備、系統等植入管理功能的框架, Spring 內部也有對JMX 的支持。
JMX Technology可以分成3層, 詳細的介紹可以參考 Lesson: Overview of the JMX Technology
  • Instrumentation
  • JMX agent
  • Remote management

Instrumentation : 定義了如何實現JMX管理資源的規範。一個JMX管理資源可以是一個Java應用、一個服
務或一個設備,它們可以用Java開發,或者至少能用Java進行包裝,並且能被置入JMX
框架中,從而成為JMX的一個管理構件(Managed Bean) ,簡稱MBean。
JMX agent : Agent 是 MBean Server 的容器,主要為了管理一系列的 MBean,並且提供的一系列
服務
Remote management : JMX 可以在許多不同的方式訪問,無論是通過現有的管理協議,如SNMP或通過
專有協議等, Each adaptor provides a view through a specific protocol
of all MBeans that are registered in the MBean server. For example, an
HTML adaptor could display an MBean in a browser.


JMX 的基本部分: 
MBean: 它是一個能代表管理资源的Java对象,它提供接口可以使這個class 有管理功能(如standard MBean,interface 中定義的方法使MBean具有管理功能)。
MBean server : 是管理MBean的一個java class,你需要向MBean server注册一個MBean,這個MBean才會具有管理功能。
JMX agent : agent是為了管理一系列的MBean,而提供的一系列的服務,agent可以利用Protocol adapters(例如HTTP 和SNMP)和connectors(RMI 和Jini)使不同的客户端可以访问MBean。


MBean的類型:
  • Standard MBeans
  • Dynamic MBeans
  • Open MBeans
  • Model MBeans
  • MXBeans

standard MBean : standard MBean 從必须定義為Interface 然后MBean必须implements 這個interface ,
它的命名也必须遵循一定的规範,如interface為Something , MBean implement 則為 
SomethingMBean.
dynamic MBean : 動態MBean是在運行期才定義它的屬性和方法,也就是說它有什麼屬性和方法
是可以動態改變的。動態MBean主要利用一些輔助類(構造函數類 
MBeanConstructorInfo、屬性類MBeanAttributeInfo、方法類
MBeanOperationInfo)來完成這個功能。
open MBean : An Open MBean is just a Dynamic MBean that follows some conventions which makes
it less powerful and convenient, but more portable. Open Mbeans Tutorial
model MBean : 與標準和動態的MBean相比,你可以不用寫的MBean類,只需使用
javax.management.modelmbean.RequiredModelMBean即可。與 DynamicMBean不
同的是,DynamicMBean管理的資源一般定義在DynamicMBean中(運行時才決定管理
那些資源),而模型MBean管理的資源並不在MBean的中,而是在外部(通常是一
個類),只有在運行時,才通過一套方法將其加入到模型MBean中

(JDK 5 內建了 jmx 的 client: jconsole。只要在 jvm 啟動時加上參數: )
-Dcom.sun.management.jmxremote 


   
  -Dcom.sun.management.jmxremote.port=1099 
  -Dcom.sun.management.jmxremote.ssl=false 
  -Dcom.sun.management.jmxremote.password.file=/mypath/jmxremote.password


簡介到這邊, 接下來主要是以Spring 來設定&設計 JMX 的部份, 如果想知道基本的JMX 部份可以參考Oracle 的Tutorials Standard MBeans ,

首先設計我們的MBean , 叫ManagerMBean.java
package com.jmxBean;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

//@ManagerResource , 實例標記為由JMX管理的資源
@ManagedResource
public class ManagerMBean extends ApplicationObjectSupport implements DisposableBean  {

 
 private int threadPoolActiveSize;
 private boolean threadPoolStatus;
 
        //將getter或setter標記為JMX的屬性
 @ManagedAttribute(description="check how many thread on ThreadPoolTaskExecutor is active...")
 public int getThreadPoolActiveSize() {
  return threadPoolActiveSize;
 }


 public void setThreadPoolActiveSize(int threadPoolActiveSize) {
  this.threadPoolActiveSize = threadPoolActiveSize;
 }

 @ManagedAttribute(description="check ThreadPoolTaskExecutor are running.")
 public boolean isThreadPoolStatus() {
  return threadPoolStatus;
 }


 public void setThreadPoolStatus(boolean threadPoolStatus) {
  this.threadPoolStatus = threadPoolStatus;
 }

        ///把方法標記為JMX的操作
 @ManagedOperation(description = "shutdown the server")
 @Override
 public void destroy() throws Exception {
  ApplicationContext application  = getApplicationContext();
  System.out.println("Trigger command from jmx remote ..... ");
//  ThreadPoolTaskExecutor task = (ThreadPoolTaskExecutor) application.getBean("taskExecutor");
//  System.out.println("getActiveCount =  "   task.getActiveCount());
 }

}
這邊implements 的是spring 的DisposableBean , 實現org.springframework.beans.factory.DisposableBean interface 的bean 允許在容器銷毀該bean 的時候獲得一次回調,下面是相關Annotation 的說明,

Table 22.2. Source-Level Metadata Types
PurposeAnnotationAnnotation Type
Mark all instances of a Class as
JMX managed resources
@ManagedResourceClass
Mark a method as a JMX operation@ManagedOperationMethod
Mark a getter or setter as one half of a JMX
attribute
@ManagedAttributeMethod (only getters and setters)
Define descriptions for operation parameters@ManagedOperationParameter and
@ManagedOperationParameters
Method

還有parameter 的設定

Table 22.3. Source-Level Metadata Parameters
ParameterDescriptionApplies to
ObjectNameUsed by MetadataNamingStrategy
to determine the ObjectName of a
managed resource
ManagedResource
descriptionSets the friendly description of the resource,
attribute or operation
ManagedResource,
ManagedAttribute,
ManagedOperation,
ManagedOperationParameter
currencyTimeLimitSets the value of the
currencyTimeLimit descriptor field
ManagedResource,
ManagedAttribute
defaultValueSets the value of the defaultValue
descriptor field
ManagedAttribute
logSets the value of the log descriptor
field
ManagedResource
logFileSets the value of the logFile
descriptor field
ManagedResource
persistPolicySets the value of the persistPolicy
descriptor field
ManagedResource
persistPeriodSets the value of the persistPeriod
descriptor field
ManagedResource
persistLocationSets the value of the
persistLocation descriptor field
ManagedResource
persistNameSets the value of the persistName
descriptor field
ManagedResource
nameSets the display name of an operation parameterManagedOperationParameter
indexSets the index of an operation parameterManagedOperationParameter


然後我們在ApplicationContext.xml 設定如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

 <bean id="taskExecutor"
  class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
  <property name="corePoolSize" value="5" />
  <property name="maxPoolSize" value="10" />
  <property name="queueCapacity" value="25" />
 </bean>

 <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
  <property name="port" value="1099" />
 </bean>

  
 <bean id="serverConnector"
  class="org.springframework.jmx.support.ConnectorServerFactoryBean">
  <property name="objectName" value="connector:name=rmi" />
  <property name="serviceUrl"
    value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector" />
    
  <!-- Set the rmi connection user,password that is in jmxremote.password file. -->
  <!-- 
  <property name="environmentMap">
   <map>
    <entry key="jmx.remote.x.password.file" value="/mypath/jmxremote.password">
    </entry>
   </map>
  </property>
   -->
 </bean>
 

 <!-- Step 1, defined MBean class (Instrumentation) -->
 <bean id="managerBean" class="com.jmxBean.ManagerMBean"></bean>



 <!-- Step 2, defined MBeanExporter class ,(JMX agent) -->
 <bean id="mBeanExporter" class="org.springframework.jmx.export.MBeanExporter">
  <property name="assembler" ref="assembler" />
  <property name="namingStrategy" ref="namingStrategy"/>
  <property name="autodetect" value="true"/>
  <!-- 
  <property name="beans">
   <map>
    <entry key="mbean:name=managerBean" value-ref="managerBean" />
   </map>
  </property>
   -->
 </bean>
 
 <bean id="jmxAttributeSource"
  class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />

 <bean id="assembler"
  class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
  <property name="attributeSource" ref="jmxAttributeSource" />
 </bean>
 
 <!-- will pick up the ObjectName from the annotation -->
 <bean id="namingStrategy"
  class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
  <property name="attributeSource" ref="jmxAttributeSource" />
 </bean>


 <!--  To create MBeanServer by JSR-160 for remote (Remote Management) -->
 <!--  
 <bean id="clientConnector"
  class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean">
  <property name="serviceUrl" value="service:jmx:rmi:///jndi/rmi://localhost:1099/myconnector" />
 </bean>
 -->
 <!--  Accessing MBeans via Proxies uses clientConnector  -->
 <!-- 
 <bean id="emailStopProxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
  <property name="objectName" value="mbean:name=managerBean" />
  <property name="proxyInterface"
   value="org.springframework.beans.factory.DisposableBean" />
  <property name="server" ref="clientConnector" />
 </bean>
 -->
</beans>

詳細說明一下整個XML的設定, 如果不是使用JDK 自帶的MBean server or web container 的話, 在Spring裡面要先register rmi , as below:
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
  <property name="port" value="1099" />
 </bean>


然後再來要設定serverConnector ,ObjectName 設為rmi , spring 會自動register MBserver connector 依照ObjectName ,下面透過ConnectorServerFactoryBean 建立一個JMXconnector, 如果要設定username,password的話必須設定environmentMap屬性
<bean id="serverConnector"
  class="org.springframework.jmx.support.ConnectorServerFactoryBean">
  <property name="objectName" value="connector:name=rmi" />
  <property name="serviceUrl"
    value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector" />
    
  <!-- Set the rmi connection user,password that is in jmxremote.password file. -->
  <!-- 
  <property name="environmentMap">
   <map>
    <entry key="jmx.remote.x.password.file" value="/mypath/jmxremote.password">
    </entry>
   </map>
  </property>
   -->
 </bean>


然後建立我們的MBean instance
<!-- Step 1, defined MBean class (Instrumentation) -->
 <bean id="managerBean" class="com.jmxBean.ManagerMBean"></bean>


再來是設定MBeanExporter , Spring 裡MBeanExporter 用來將你的MBean 註冊到JMX MBeanServer上,
assembler 這邊指向一個MetadataMBeanInfoAssembler,屬性指向一個AnnotationJmxAttributeSource,看到Annotation也就知道是用jdk1.5的Annotation了。用途是将你所需要變成MBean class的信息提取出来,並生成MBean。Spring大大简化了MBean的實現方式。namingStrategy這邊reference 是 MetadataNamingStrategy , 代表會使用 source level metadata來獲得 ObjectName.

<!-- Step 2, defined MBeanExporter class ,(JMX agent) -->
 <bean id="mBeanExporter" class="org.springframework.jmx.export.MBeanExporter">
  <property name="assembler" ref="assembler" />
  <property name="namingStrategy" ref="namingStrategy"/>
  <property name="autodetect" value="true"/>
  <!-- 
  <property name="beans">
   <map>
    <entry key="mbean:name=managerBean" value-ref="managerBean" />
   </map>
  </property>
   -->
 </bean>

其中mark 住的clientConnector 跟 emailStopProxy 是建立一個clientConnector 指向一個jmx rmi 的serverConnector , 再透過proxy 存取MBean , 相關設定可以參考Spring 的JMX設定,

再來透過jconsole 連線到server , 位置就是serverConnection的設定
service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector, as below :

連線之後就能看到ManagerMBean的information ,



Reference:

Spring Doc : 22. JMX
Trail: Java Management Extensions (JMX): Table of Contents
Quickly Exposing Spring Beans as JMX MBeans
Spring by Example : JMX
Jconsole Remote Management

沒有留言:

張貼留言