「進階之路」揭開JMX的神祕面紗

「進階之路」揭開JMX的神秘面紗

No.1 相關概念

JMX(Java Management Extensions)是一個為應用程序植入管理功能的框架。JMX是一套標準的代理和服務,實際上,用戶可以在任何Java應用程序中使用這些代理和服務實現管理。

JMX 要管理的對象是什麼呢,是資源。

什麼是資源,資源是指企業中的的各種應用軟件和平臺,舉例來說,一個公司內部可能有許多應用服務器、若干 Web 服務器、一臺或多臺的數據庫服務器及文件服務器等等,

那麼,如果我們想監視數據庫服務器的內存使用情況,或者我們想更改應用服務器上 JDBC 最大連接池的數目,但我們又不想重啟數據庫和應用服務器,這就是典型意義上的資源管理,

即對我們的資源進行監視 (Monitoring ,查看 ) 和管理 (Management ,更改 ) ,這種監視和更改不妨礙當前資源的正常運行。

對資源進行適當的監測和管理,可以讓我們的 IT 資源儘可能的平穩運行,可以為我們的客戶提供真正意思上的 24 × 7 服務。

在資源耗盡或者在硬件出故障之前,我們就可以通過管理工具監測到,並通過管理工具進行熱調整和插拔。

應用場景:中間件軟件WebLogic的管理頁面就是基於JMX開發的,而JBoss則整個系統都基於JMX構架。

No.2 JMX架構

JMX的結構一共分為三層:

  • 基礎層:主要是MBean,被管理的資源。
  • 代理層:MBeanServer,主要是提供對資源的註冊和管理。
  • 管理層:提供遠程訪問的入口。

2.1、基礎層(MBean)

該層包含 MBeans 及這些 MBeans 所管理的資源, MBeans 是一個 Java 對象,它必須實現 JMX 規範中規定的接口。

按照 JMX 規範,在 MBeans 對象的接口中,我們可以指定管理層可以訪問資源的哪些屬性,可以調用資源的哪些方法,並且,在資源的屬性發生變化時,我們的 MBeans 可以發出消息,通知對這些屬性變化感興趣的其它

對象。

JMX 規範定義了四種 MBeans ,它們分別是標準 MBeans(Standard MBeans) 、動態 MBeans(Dynamic MBeans) 、開放 MBeans(Open MBeans) 和模態 MBeans(Model MBeans) 。

  • 標準的MBean(Standard MBeans) Standard MBean這種類型的MBean最簡單,它能管理的資源(包括屬性,方法,時間)必須定義在接口中,然後MBean必須實現這個接口。它的命名也必須遵循一定的規範,例如我們的MBean為Hello,則接口必須為HelloMBean。 為了實現Standard MBean,必須遵循一套繼承規範:
  • 必須為每一個MBean定義一個接口,且該接口名稱必須=被管理的資源名+"MBean";
  • 該接口的實現類的名稱必須是被管理的資源名;
  • 將它們註冊到MBeanServer中

2.2、代理層(Agent)

代理層的目的就是要把 MBeans 中實現的接口暴露給管理層,該層通常由 MBean Server 和 Agent Services 構成。

  • MBean Server 就是一個 MBeans 對象註冊器,所有的資源MBeans都註冊到這個MBean Server,對象管理器或者其它的管理層應用程序可以通過訪問MBean Server,
  • 從而可以訪問 MBean Server中註冊的MBeans,當然也就可以監視和管理和這些 MBeans 綁定的資源。


2.3、管理層(Manager)

又稱之為分佈式服務層 (Distributed Services) ,顧名思義,該層主要包含一些管理應用程序,這些程序可以訪問和操作 JMX 代理層 (Agent Level) 。

No.3 示例程序

3.1、需求

在企業內部應用中,假設我們需要對應用所使用的數據庫進行動態的切換(現實中應該沒有這樣的需求,這裡只是為了舉例),而且在切換過程中應用不能暫停服務,即必須保證7*24小時的運行。

3.2、設計

一般情況下,系統在切換數據庫時,需要對數據庫配置信息進行修改,然後重啟服務,加載新的數據庫配置信息。但是我們這裡不能這樣做,因為不能暫停服務。

那我們就使用JMX技術來實現吧。

3.3、編碼

  • 編寫數據庫配置信息MBean接口
package io.xiaojl.jmx;
/**
*

Title: DBConfigMBean


*
*

Description: 數據庫配置資源的接口類


*
* @author jilong.xiao
* @date 2018年9月11日
*/
public interface DBConfigMBean {
String getDriverClass();
String getUrl();
String getUsername();
String getPassword();
void setDriverClass(String driverClass);
void setUrl(String url);
void setUsername(String username);
void setPassword(String password);
}
  • 編寫接口實現類
package io.xiaojl.jmx;
/**
*

Title: DBConfig


*
*

Description: MBean(資源)


*
* @author jilong.xiao
* @date 2018年9月11日
*/
public class DBConfig implements DBConfigMBean {
private String driverClass;
private String url;
private String username;
private String password;
@Override
public String getDriverClass() {
return this.driverClass;
}
@Override
public String getUrl() {
return this.url;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public void setDriverClass(String driverClass) {
this.driverClass = driverClass;
}
@Override
public void setUrl(String url) {
this.url = url;
}
@Override
public void setUsername(String username) {
this.username = username;
}
@Override
public void setPassword(String password) {
this.password = password;
}
}
  • 編寫Agent類
package io.xiaojl.jmx;
import java.io.IOException;

import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.util.Iterator;
import java.util.Set;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
/**
*

Title: DBConfigAgent


*
*

Description: 將MBean註冊到MBeanServer


*
* @author jilong.xiao
* @date 2018年9月11日
*/
public class DBConfigAgent {
public static void main(String[] args) throws MalformedObjectNameException,
InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException, IOException {
// 首先建立一個MBeanServer,MBeanServer用來管理我們的MBean,通常是通過MBeanServer來獲取我們MBean的信息
MBeanServer mbServer = ManagementFactory.getPlatformMBeanServer();
String domainName = "OurMBean";
ObjectName on = new ObjectName(domainName+":name=DBConfigMBean");
//註冊到MBeanServer
DBConfigMBean dbConfigMBean = new DBConfig();
mbServer.registerMBean(dbConfigMBean , on);
System.out.println("---DBConfigMBean註冊完成----");
Set beans = mbServer.queryMBeans(null, null);
Iterator it = beans.iterator();
while(it.hasNext()){
ObjectInstance next = it.next();
ObjectName name = next.getObjectName();
System.out.println("\t已經註冊的MBean有:"+name);

}
//註冊一個端口,綁定url後,客戶端就可以使用rmi通過url方式來連接JMXConnectorServer
LocateRegistry.createRegistry(9001);
String serviceURL = "service:jmx:rmi:///jndi/rmi://localhost:9001/jmxrmi";
JMXServiceURL url = new JMXServiceURL(serviceURL);
//創建JMXConnectorServer
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbServer);
//啟動
cs.start();
System.out.println("---JMXConnectorServer啟動完成----");
}
}
  • 運行服務端
---OurMBean註冊完成----
已經註冊的MBean有:java.lang:type=OperatingSystem
已經註冊的MBean有:java.lang:type=Compilation
已經註冊的MBean有:java.lang:type=MemoryPool,name=PS Old Gen
已經註冊的MBean有:JMImplementation:type=MBeanServerDelegate
已經註冊的MBean有:java.lang:type=Memory
已經註冊的MBean有:java.lang:type=MemoryPool,name=PS Perm Gen
已經註冊的MBean有:java.lang:type=Runtime
已經註冊的MBean有:java.nio:type=BufferPool,name=direct
已經註冊的MBean有:java.lang:type=GarbageCollector,name=PS MarkSweep
已經註冊的MBean有:java.nio:type=BufferPool,name=mapped
已經註冊的MBean有:java.lang:type=Threading
已經註冊的MBean有:java.lang:type=GarbageCollector,name=PS Scavenge
已經註冊的MBean有:com.sun.management:type=HotSpotDiagnostic
已經註冊的MBean有:java.lang:type=ClassLoading
已經註冊的MBean有:java.lang:type=MemoryPool,name=PS Survivor Space

已經註冊的MBean有:java.lang:type=MemoryManager,name=CodeCacheManager
已經註冊的MBean有:java.lang:type=MemoryPool,name=Code Cache
已經註冊的MBean有:java.lang:type=MemoryPool,name=PS Eden Space
已經註冊的MBean有:java.util.logging:type=Logging
已經註冊的MBean有:OurMBean:name=DBConfigMBean
---JMXConnectorServer啟動完成----
  • 編寫管理端
package io.xiaojl.jmx;
import java.io.IOException;
import java.util.List;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
/**
*

Title: JMXClient


*
*

Description: 客戶端:可以獲取jmx connector,並展示HelloMBean的一些信息,屬性,及調用其方法


*
* @author jilong.xiao
* @date 2018年9月11日
*/
public class JMXClient {
static final String serviceURL = "service:jmx:rmi:///jndi/rmi://localhost:9001/jmxrmi";
public static void main(String[] args) throws IOException, MalformedObjectNameException,
AttributeNotFoundException, InstanceNotFoundException,

MBeanException, ReflectionException {
JMXServiceURL url = new JMXServiceURL(serviceURL);
JMXConnector connect = JMXConnectorFactory.connect(url);
MBeanServerConnection connection = connect.getMBeanServerConnection();
System.out.println("獲取MBeanServer中的所有Domain:");
String[] domains = connection.getDomains();
for(String domain:domains){
System.out.println("\tdomain:"+domain);
}
String domainName = "OurMBean";
ObjectName on = new ObjectName(domainName+":name=DBConfigMBean");
System.out.println("設置"+on+"的相關資源:注意屬性名稱首字母大寫");
AttributeList attributes = new AttributeList();
attributes.add(new Attribute("DriverClass", "com.mysql.jdbc.Driver"));
attributes.add(new Attribute("Url", "jdbc:mysql://127.0.0.1:3306/xiaojldb"));
attributes.add(new Attribute("Username", "root"));
attributes.add(new Attribute("Password", "root"));
connection.setAttributes(on, attributes);
System.out.println("獲取"+on+"的相關資源:");
//Object driverClass = connection.getAttribute(on, "DriverClass");
//System.out.println("\tdriverClass="+driverClass);
String[] attnames = {"DriverClass","Url","Username","Password"};
AttributeList attList = connection.getAttributes(on, attnames);
List list = attList.asList();
for(Attribute att: list){
System.out.println("\t"+att.getName()+"="+att.getValue());
}
}
}
  • 運行管理端
獲取MBeanServer中的所有Domain:
domain:OurMBean
domain:java.nio
domain:JMImplementation
domain:com.sun.management
domain:java.lang
domain:java.util.logging
設置OurMBean:name=DBConfigMBean的相關資源:注意屬性名稱首字母大寫

獲取OurMBean:name=DBConfigMBean的相關資源:
DriverClass=com.mysql.jdbc.Driver
Url=jdbc:mysql://127.0.0.1:3306/xiaojldb
Username=root
Password=root

原文出處:GitShare

商業用途請與作者聯繫!


分享到:


相關文章: