Spring中的JMX(Java管理擴充)支援提供了一些特性,使你能夠輕松、透明地将Spring應用程式內建到JMX基礎設施中。
具體來說,Spring的JMX支援提供了四個核心功能:
- 将任何Spring Bean自動注冊為 JMX MBean。
- 控制bean管理接口的靈活機制。
- 通過遠端JSR-160連接配接器聲明性地公開MBeans。
- 本地和遠端MBean資源的簡單代理。
這些特性被設計成在不将應用程式元件耦合到Spring或JMX接口和類的情況下工作。實際上,在大多數情況下,應用程式類不需要知道Spring或JMX,就可以利用spring jmx特性。
一、JMX介紹
JMX(Java Management Extensions,即Java管理擴充)是一個為應用程式、裝置、系統等植入管理功能的架構。JMX可以跨越一系列異構作業系統平台、系統體系結構和網絡傳輸協定,靈活的開發無縫內建的系統、網絡和服務管理應用。
JMX架構圖

從圖中我們可以看到,JMX的結構一共分為三層:
基礎層:主要是Mbean,被管理的java bean
Mbean分為如下四種:
- standard MBean:這種類型的MBean最簡單,它能管理的資源(包括屬性,方法,時間)必須定義在接口中,然後MBean必須實作這個接口。它的命名也必須遵循一定的規範,例如我們的MBean為Hello,則接口必須為HelloMBean。
- dynamic MBean:必須實作javax.management.DynamicMBean接口,所有的屬性,方法都在運作時定義。
- model MBean:與标準和動态MBean相比,你可以不用寫MBean類,隻需使用javax.management.modelmbean.RequiredModelMBean即可。RequiredModelMBean實作了ModelMBean接口,而ModelMBean擴充了DynamicMBean接口,是以與DynamicMBean相似,Model MBean的管理資源也是在運作時定義的。與DynamicMBean不同的是,DynamicMBean管理的資源一般定義在DynamicMBean中(運作時才決定管理那些資源),而model MBean管理的資源并不在MBean中,而是在外部(通常是一個類),隻有在運作時,才通過set方法将其加入到model MBean中。
适配層:MbeanServer,提供對資源的注冊和管理
接入層: 提供遠端通路的入口
standard MBean示範
根據standard MBean的要求,我們首先要定義一個MBean接口,接口的命名規範以具體的實作類為字首,為了後續可以注冊到MBean Server中。
package jmx;
public interface HelloMBean
{
public String getName();
public void setName(String name);
public String getAge();
public void setAge(String age);
public void helloWorld();
public void helloWorld(String str);
public void getTelephone();
}
定義一個實作類
package jmx;
/*
* 該類名稱必須與實作的接口的字首保持一緻(即MBean前面的名稱
*/
public class Hello implements HelloMBean
{
private String name;
private String age;
public void getTelephone()
{
System.out.println("get Telephone");
}
public void helloWorld()
{
System.out.println("hello world");
}
public void helloWorld(String str)
{
System.out.println("helloWorld:" + str);
}
public String getName()
{
System.out.println("get name 123");
return name;
}
public void setName(String name)
{
System.out.println("set name 123");
this.name = name;
}
public String getAge()
{
System.out.println("get age 123");
return age;
}
public void setAge(String age)
{
System.out.println("set age 123");
this.age = age;
}
}
定義agent層
package jmx;
import java.lang.management.ManagementFactory;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class HelloAgent
{
public static void main(String[] args) throws JMException, Exception
{
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName helloName = new ObjectName("jmxBean:name=hello");
//create mbean and register mbean
server.registerMBean(new Hello(), helloName);
Thread.sleep(60*60*1000);
}
}
- 通過工廠類擷取Mbean Server,用來做Mbean的容器
- ObjectName的取名規範:域名:name=Mbean名稱,其中域名和Mbean的名稱可以任取。這樣定義後,我們可以唯一标示我們定義的這個Mbean的實作類了
- 最後将Hello這個類注冊到MbeanServer中,注入需要建立一個ObjectName類,我們可以用jdk自帶的Jconsole用來觀察,可以設定屬性值和調用相關方法。
通知(Notification)
MBean之間的通信是必不可少的,Notification起到了在MBean之間溝通橋梁的作用。JMX 的通知由四部分組成:
- Notification這個相當于一個資訊包,封裝了需要傳遞的資訊
- Notification broadcaster這個相當于一個廣播器,把消息廣播出。
- Notification listener 這是一個監聽器,用于監聽廣播出來的通知資訊。
- Notification filiter 這個一個過濾器,過濾掉不需要的通知。這個一般很少使用。
保留Hello及HelloMBean,增加如下:
package jmx;
public interface JackMBean
{
public void hi();
}
package jmx;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
public class Jack extends NotificationBroadcasterSupport implements JackMBean
{
private int seq = 0;
public void hi()
{
//建立一個資訊包
Notification notify =
//通知名稱;誰發起的通知;序列号;發起通知時間;發送的消息
new Notification("jack.hi",this,++seq,System.currentTimeMillis(),"jack");
sendNotification(notify);
}
}
這裡的類Jack不僅實作了MBean接口,還繼承了NotificationBroadcasterSupport。jack在這裡建立并發送了一個消息包。
package jmx;
import javax.management.Notification;
import javax.management.NotificationListener;
public class HelloListener implements NotificationListener
{
public void handleNotification(Notification notification, Object handback)
{
if(handback instanceof Hello)
{
Hello hello = (Hello)handback;
hello.printHello(notification.getMessage());
}
}
}
對HelloAgent做以下修改
package jmx;
import java.lang.management.ManagementFactory;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class HelloAgent
{
public static void main(String[] args) throws JMException, Exception
{
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName helloName = new ObjectName("yunge:name=Hello");
Hello hello=new Hello();
server.registerMBean(hello, helloName);
Jack jack = new Jack();
server.registerMBean(jack, new ObjectName("jack:name=Jack"));
jack.addNotificationListener(new HelloListener(), null, hello);
Thread.sleep(500000);
}
}
我們利用jconsole調用jack的hi方法,這裡當jack發出消息後,Notification被廣播至所有的MBean,當有MBean屬于Hello類時則調用Hello的printHello()方法。
JMX的應用
在linux下利用jmx監控Tomcat,在catlina.sh中進行一些環境變零的配置
- Dcom.sun.management.jmxremote=true:相關 JMX 代理偵聽開關
- Djava.rmi.server.hostname:伺服器端的IP
- Dcom.sun.management.jmxremote.port=29094:相關 JMX 代理偵聽請求的端口
- Dcom.sun.management.jmxremote.ssl=false:指定是否使用 SSL 通訊
- Dcom.sun.management.jmxremote.authenticate=false:指定是否需要密碼驗證
二、将bean導出到JMX
Spring的JMX架構中的核心類是MBeanExporter。這個類負責擷取Spring Bean并将它們注冊到JMX MBeanServer。例如,考慮以下類:
package org.springframework.jmx;
public class JmxTestBean implements IJmxTestBean {
private String name;
private int age;
private boolean isSuperman;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int add(int x, int y) {
return x + y;
}
public void dontExposeMe() {
throw new RuntimeException();
}
}
要将此bean的屬性和方法公開為MBean的屬性和操作,可以在配置檔案中配置MBeanExporter類的執行個體并傳入bean,如下例所示:
<beans>
<!-- this bean must not be lazily initialized if the exporting is to happen -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
前面配置片段中的相關bean定義是exporter bean。beans屬性告訴MBeanExporter哪些bean必須導出到JMX MBeanServer。在預設配置中,bean映射中每個條目的鍵被用作相應條目值引用的bean的對象名。你可以更改此行為,如控制bean的ObjectName執行個體中所述。
使用此配置,testBean bean将作為ObjectName下的MBean公開bean:name=testBean1。預設情況下,bean的所有公共屬性都作為屬性公開,所有公共方法(繼承自對象類的方法除外)作為操作公開。
MBeanExporter是一個生命周期bean。預設情況下,在應用程式生命周期中盡可能晚地導出MBean。你可以通過設定autoStartup标志來配置導出發生的階段或禁用自動注冊。
建立MBeanServer
上一節中顯示的配置假定應用程式運作在一個(且隻有一個)已運作MBeanServer的環境。在本例中,Spring嘗試定位正在運作的MBeanServer,并在該伺服器上注冊bean(如果有的話)。當你的應用程式在具有自己的MBeanServer的容器(如Tomcat或ibm websphere)中運作時,此行為非常有用。
但是,這種方法在獨立環境中或在不提供MBeanServer的容器中運作時沒有用處。為了解決這個問題,你可以通過添加org.springframework.jmx.support.MBeanServerFactoryBean類到你的配置。你還可以通過将MBeanExporter執行個體的server屬性的值設定為MBeanServerFactoryBean傳回的MBeanServer值來確定使用特定的MBeanServer,如下例所示:
<beans>
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
<!--
this bean needs to be eagerly pre-instantiated in order for the exporting to occur;
this means that it must not be marked as lazily initialized
-->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="server" ref="mbeanServer"/>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
在前面的示例中,MBeanServer的執行個體由MBeanServerFactoryBean建立,并通過server屬性提供給MBeanExporter。當你提供自己的MBeanServer執行個體時,MBeanExporter不會嘗試定位正在運作的MBeanServer,而是使用提供的MBeanServer執行個體。。要使其正常工作,你的類路徑上必須有一個JMX實作。
重用已存在的MBeanServer
如果未指定伺服器,則MBeanExporter将嘗試自動檢測正在運作的MBeanServer。這适用于大多數環境,其中隻使用一個MBeanServer執行個體。但是,當存在多個執行個體時,導出程式可能會選擇錯誤的伺服器。在這種情況下,你應該使用MBeanServer agentId來訓示要使用哪個執行個體,如下例所示:
<beans>
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<!-- indicate to first look for a server -->
<property name="locateExistingServerIfPossible" value="true"/>
<!-- search for the MBeanServer instance with the given agentId -->
<property name="agentId" value="MBeanServer_instance_agentId>"/>
</bean>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="server" ref="mbeanServer"/>
...
</bean>
</beans>
對于現有MBeanServer具有動态的agentId的平台或情況,應使用factory方法,如下例所示:
<beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="server">
<!-- Custom MBeanServerLocator -->
<bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/>
</property>
</bean>
<!-- other beans here -->
</beans>
延遲初始化MBeans
如果你使用MBeanExporter配置bean,該MBeanExporter也配置為延遲初始化,MBeanExporter不會破壞這個契約,并且避免執行個體化bean。相反,它向MBeanServer注冊一個代理,并延遲從容器擷取bean,直到對代理進行第一次調用。
MBeans的自動注冊
任何通過MBeanExporter導出的、已經是有效MBean的bean都将按原樣在MBeanServer中注冊,而無需Spring的進一步幹預。通過将autodetect屬性設定為true,可以使MBeanExporter自動檢測到mbean,如下例所示:
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="autodetect" value="true"/>
</bean>
<bean name="spring:mbean=true" class="org.springframework.jmx.export.TestDynamicMBean"/>
控制注冊行為
考慮這樣一個場景:考慮這樣一個場景:Spring MBeanExporter嘗試使用ObjectName向MBeanServer注冊MBeanbean:name=testBean1。如果已經在同一個ObjectName下注冊了一個MBean執行個體,預設行為是fail(并抛出InstanceAlreadyExistsException)。
你可以精确地控制當MBean注冊到MBeanServer時發生的事情。Spring的JMX支援允許三種不同的注冊行為來控制注冊行為,當注冊過程發現一個MBean已經注冊在同一個ObjectName下時。下表總結了這些注冊行為:
- FAIL_ON_EXISTING:這是預設的注冊行為。如果一個MBean執行個體已經在同一個ObjectName下注冊,那麼正在注冊的MBean不會被注冊,并且會抛出InstanceAlreadyExistsException。現有的MBean不受影響。
- IGNORE_EXISTING:如果一個MBean執行個體已經在同一個ObjectName下注冊,那麼正在注冊的MBean不會被注冊。現有的MBean不會受到影響,也不會引發異常。在多個應用程式希望在共享MBeanServer中共享一個公共MBean的設定中,這很有用。
- REPLACE_EXISTING:如果一個MBean執行個體已經在同一個ObjectName下注冊,那麼先前注冊的現有MBean将被登出,而新的MBean将在其位置上注冊(新的MBean實際上替換了以前的執行個體)。
上述中的值定義為RegistrationPolicy類上的枚舉。如果要更改預設注冊行為,則需要将MBeanExporter定義上的registrationPolicy屬性的值設定為這些值之一。
<beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="registrationPolicy" value="REPLACE_EXISTING"/>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
三、控制bean的管理接口
時刻與技術進步,每天一點滴,日久一大步!!!
本部落格隻為記錄,用于學習,如有冒犯,請私信于我。