文章目录
1.简介
分布式程序,设备,服务在企业以各种方式出现,为了能够正常访问这些服务或者程序,需要对程序以及程序部署的设备运维。那么就要说到资源管理(Resource Management),资源管理是个管理概念,用于程序或者资源管理的计划和工具。企业中资源管理意味着用工具报告程序和硬件的健康状况,基于此来对系统故障和重大事件作出快速处理。我们期待的理想管理环境具备以下两点:
- 具有主动处理问题的能力,管理系统持续监控设备和应用程序的健康状况。如果某个设备出现宕机,管理系统可以发现另外一个设备,将服务重新路由到备用的设备上面,并且通知管理员。
- 不仅知道应用程序的健康状况,并且了解程序内部的事物以及有决策能力。例如根据用户的请求数量,能进行调整线程的数量。
实际应用中,我们目的并不是为了获取一个完美的管理环境,而是为了构建和维护一个可行性的管理系统。此类管理系统需要考虑下面几个问题:
- 监控平台和硬件的系统运行状况,包含web和应用程序服务器以及部署的硬件。
- 配置应用程序级别的资源。
- 收集应用程序统计信息。如用户的访问次数或者交易成交次数,以及网站被恶意攻击次数等。
- 调试选项。例如:程序出现问题时,通过管理接口,打开调试并将日志输出到控制台,此时可以在不必关闭应用程序查看程序的功能。
- 监控服务器的性能。例如:web应用程序的运行状况和总体负载,主服务器负载过大,可以将分流量到备份服务器。
以上几点涵盖了应用程序监控和管理环境的需求,在JMX出现之前,构建这样的环境是困难的,需要不同工具进行来管理系统,这造成很多麻烦。JMX可以创建一个更加低廉和灵活的管理环境,全称是java管理扩展(Java Management Extensions
)。JMX技术在Java 2平台和标准版(J2SE)5.0中被引入成为java平台一部分。由于基于Java,JMX拥有Java平台的一些优势(如可移植性),此外还有其他的优点:
1.易用性:编写JMX代码比较简单和直接,尤其如果程序是基于java语言时,构建管理环境更加容易。
2.利用现有的技术:构建JMX管理环境,不必抛弃现有的管理结构,现有的管理工具可以引入到JMX技术中,JMX提供了与任何协议(SNMP或HTTP)建立通信的能力。
3.组件化:JMX允许以组件化方式来构建管理解决方案。如公开整个设备或者应用程序,或者仅公开其配置功能的一个子集。
4.警报,事件和统计信息:JMX可以检测和推送应用程序的当前健康状态信息以及有用的统计信息。此外还提供强大的分发java对象能力的通知系统。JMX可以发出数据元素(警报和系统事件),并且发送解释数据的机制。如发送带有处理器负载状态的通知,管理系统可以根据负载和压力状态做出决策。
2.JMX基本术语
———可管理资源(Manageable resource)
可管理资源可以是任何应用程序,设备或者由java访问和封装的实体,甚至可以是网络设备(如打印机)。应用程序可以公开组件,API或者其他资源,然后由JMX环境管理。可管理资源是由JMX MBean管理的实体。
———管理构件(MBean,Managed Bean)
MBean是一个java类,实例化的MBean是java对象,它满足JMX规范所规定的命名和继承标准,通过公开管理接口来处理和访问可管理资源。MBean位于JMX组件MBean服务器(MBean Server)中,它的管理接口主要由下面几个部分组成:
- 能被访问的属性
- 能被执行的操作(方法)
- 能发出的通知事件
- 构造器
MBean类型以下几种:
MBean类型 | 描述 |
---|---|
Standard MBean | 在Standard MBean中需要定义为一个ClassNameMBean 的接口和实现此接口的名为ClassName 的java类。如接口为HelloMBean ,需要一个名称为Hello 的java类来实现此接口,MBean接口中包含了要公开的属性和方法 |
Dynamic MBean | Dynamic MBean需要实现接口java.management.DynamicMBean ,它是在运行时明确管理接口,并且管理的接口在运行时通过getMBeanInfo() 来获取一个MBeanInfo 对象,MBeanInfo 中包含管理信息与受管理资源交互所有信息。 |
Model MBean | 相比Standard MBean和Dynamic MBean,ModelBean不需要写MBean类。Model MBean是JMX agent的一部分,通过javax.management.modelmbean.RequiredModelBean 来定义。与Dynamic MBean一样,Model MBean也是在运行时确定管理接口,实际上RequiredModelMBean 类实现了ModelMBean 接口,而ModelMBean 接口继承了DynamicMBean 接口。不同于Dynamic MBean的是,Model MBean的管理接口是在MBean之外定义(如定义在程序中),通过Setter方法插入到MBean中。 |
Open MBean | Open MBean是更加灵活和更加丰富的自我描述MBean,其核心是用于描述方法参数,返回类型和属性的基础数据类型。Open MBean同样实现了Dynamic MBean接口,并且从DynamicMBean接口继承了getMBeanInfo() 方法,在Open Mbean中此方法返回了包含所有描述信息的OpenMBeanInfo 对象。Open MBean在管理环境很有用,因为用户可以提前了解与MBean交互的所有对象的类型 |
MXBean | MXBean是一种特殊的MBean,它只引用一组预定义的数据类型,通过这种方式,确保MBean可以被任何客户机(包括远程客户机)使用。与Standard MBean一样,需要编写一个接口称为ClassNameMXBean 和一个实现该接口的java类,与Standard MBean不同的是,java实现类不需要名称必须是ClassName |
———MBean服务器(MBean Server)
MBean Server是管理一组MBean的Java类,是JMX管理环境的核心,充当查找MBean的注册表。MBean服务器公开任何已注册MBean的管理接口,但不公开对象的引用。MBean Server目的是为用户提供一个相同的接口,不管访问的MBean类型是什么。此外还提供了查找MBean以及其他对象注册为通知监听器的方法。
———JMX代理(JMX agent)
JMX代理是MBean Server的容器,提供了一系列管理MBean的服务。JMX代理提供可以创建MBean之间关系,动态加载类,简单监控服务以及计时器的服务。
代理可以有一系列的协议适配器(protocol adapters)
和连接器(connectors)
,协议适配器和连接器是Java类,通常是MBean,他们的目的都是为管理实体打开JMX代理。协议适配器可以映射一个外部协议,而连接器可以将JMX代理暴露给客户端。远程通过不同协议(如SNMP或者HTTP)或者客户端通过连接器(RMIConnectors)与JMX代理进行通信。JMX代理可以被各种不同管理协议和工具使用。
———协议适配器(protocol adapters)和连接器(connectors)
协议适配器和连接器是JMX代理中的对象,它们将JMX代理暴露给管理应用程序和协议。其中协议适配器是JMX中的一个组件,是代理中的单个对象。例如:使用JMX代理中的SNMP适配器
对象将SNMP映射给JMX代理,客户端通过SNMP协议可访问JMX代理。而连接器
需要两个组件,一个位于JMX代理中,一个在客户端程序中,客户端使用连接器与服务端的连接器进行连接并与JMX代理进行通信。JMX代理可以有任意数量的适配器和连接器,可以利用新的工具或者现有的管理协议和应用程序访问JMX代理。当前已经实现或者将要实现的协议适配器和连接器有:
- RMI连接器
- HTTP连接器
- SNMP协议适配器
- IIOP协议适配器
- HTML协议适配器
———管理应用程序(Management application)
管理应用程序是与任何数量的JMX代理交互的任何用户应用程序。JMX代理可以与设计使用JMX技术的管理用程序或者不使用JMX技术的管理应用程序一起工作。兼容JMX技术的管理应用程序可以利用JMX的高级功能。利用适配器和连接器,JMX代理可以非JMX管理程序进行交互。
———通知(Notification)
一个管理系统提供了管理接口允许代理对其管理资源进行控制或者配置。然而对于复杂的分布式系统来说,这些接口只是提供了一部分功能,通常管理应用程序需要对状态变化或者当特别情况发生变化时做出反映,因此JMX定义了通知模型。JMX通知是由MBean发出的java对象和MBean服务器发出的事件,警报或者一般信息组成。其他的MBean或者Java对象可以注册为监听器来接收通知。通知模型包含以下几个部分:
Notification
接口——一个通用的事件类型,该类标识事件的类型,可以被直接使用,也可以根据传递的事件的需要而被扩展。NotificationListener
接口——接受通知的对象需实现此接口。NotificationFilter
接口——作为通知过滤器的对象需实现此接口,为通知监听者提供了一个过滤通知的过滤器。NotificationBroadcaster
接口——通知的发送者需实现此接口,该接口允许希望得到通知的监听者注册。
发送一个通用类型的通知,任何一个监听者都会得到该通知。因此,监听者需提供过滤器来选择所需要接受的通知。任何类型的MBean,标准的或动态的,都可以作为一个通知发送者,也可以作为一个通知监听者,或两者都是。
———辅助元数据(MetaData)
辅助元数据类用来描述MBean。辅助元数据类不仅被用来内省Standard MBean
(内省即根据MBean接口中方法来判断公开是属性还是方法),也被Dynamic MBean
用来进行自我描述。这些类根据属性、操作、构建器和通知描述了管理接口。JMX代理通过这些元数据类管理所有MBean,而不管MBean的类型。部分的辅助元类如下:
MBeanInfo
——包含了属性、操作、构建器和通知的信息。MBeanFeatureInfo
——为下面类的超类。MBeanAttributeInfo
——用来描述MBean中的属性。MBeanConstructorInfo
——用来描述MBean中的构建器。MBeanOperationInfo
——用来描述MBean中的操作(方法)。MBeanParameterInfo
——用来描述MBean操作或构建器的参数。MBeanNotificationInfo
——用来描述MBean发出的通知。
3.JMX架构
JMX架构主要分为三个部分:
3.1 分布层(Distributed layer)
分布层是JMX体系结构中的最外层,这一层负责JMX代理的外部访问。主要有两种分布式交互,第一种是使用适配器对象(Adaptor)
实现交互,适配器通过不同的协议(如HTTP或者SNMP协议)提供了MBean的可见性。第二种是使用连接器(Connector)
组件交互,它将JMX代理的API暴露给其他的分布式技术,如Java RMI。适配器和连接器在JMX环境提供了相同的功能。因此在分布式层中需要一个允许将客户机连接到代理的组件,该组件可以通过Web浏览器、Java RMI或者SNMP将用户连接到代理。当与代理建立连接时,用户可以使用管理工具与代理中注册的MBean进行交互,此时将会进入到JMX架构体系中的代理层。
3.2 代理层( Agent layer)
代理层主要组件的是MBean Server
,一个MBean Server是一个java对象,它充当了MBean的注册表,是JMX代理的核心。此外代理层还提供了四个代理服务,使得管理MBean更加容易:计时器
,监控
,动态MBean加载
和关系服务
。代理层提供了从应用程序访问受管理资源的权限。JMX代理可以在承载资源机器上嵌入的JVM中运行,也可以远程定位。代理层不需要了解它公开的资源或者用于公开MBean的程序。它充当了处理MBean的服务,并允许连接器或者适配器来操作MBean。找到MBean时,此时将会进入设备层。
3.3 设备层(Instrumentation layer )
设备层是最接近受管理资源的层,由注册在JMX代理的MBean
组成。MBean允许通过JMX代理来管理资源,它公开了一个底层资源的配置和功能。JMX管理的资源可以是Java应用、服务或者设备,如果资源不是用java语言开发,MBean可充当代理到资源的翻译器。实际上MBean就是一个轻量级的类,它知道如何使用,获取和操作管理资源,然后向JMX代理提供访问。此外MBean必须遵守JMX规范中定义的设计模式和接口,这样做可以确保所有MBean以标准方式提供受管理的资源。
4.简单案例
先看一个简单的案例,此处我们使用Standard MBean
,一个Standard MBean
接口命名标准是ClassNameMBean
。MBean接口可以公开属性或者方法,其中属性是通过Setter方法或者getter方法来公开(Setter表示可写,getter表示可读)。除此之外,其他非标准Setter和getter方法公开的是方法。如下面这个HelloMBean接口公开了属性greeting(即可读又可写)和方法printGreeting(),printGreeting(String name).
步骤一:创建一个MBean接口,接口名称要符合Standard MBean
的规范,即ClassNameMBean
。
public interface HelloMBean {
public String getGreeting();
public void setGreeting(String greeting);
public void printGreeting();
public void printGreeting(String name);
}
步骤二:创建一个Java类来实现MBean接口,实现类的名称必须是ClassNameMBean
中的ClassName
。
public class Hello implements HelloMBean {
private String greeting;
public String getGreeting() {
return greeting;
}
public void setGreeting(String greeting) {
this.greeting = greeting;
}
public void printGreeting() {
System.out.println(greeting);
}
public void printGreeting(String name) {
System.out.println(name+"---"+greeting);
}
}
步骤三:创建一个JMX代理。
JMX代理里面可有多个MBeanServer,而MBean需要注册到MBeanServer里面。ObjectName用于标识一个MBean,因此名称不能一样,其格式为:domain:key=value
。此外JMX代理可以有适配器(Adaptor)和连接器(Connector),本例中创建了HtmlAdaptorServer
和JMXConnectorServer
,前者可以使用网页访问,后者可以在客户端使用JMXConnector
来连接。
public class HelloAgent {
public static void main(String[] args) throws Exception {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
//参数中的HelloMBean是domainName
ObjectName helloName = new ObjectName("HelloMBean:name=Hello");
server.registerMBean(new Hello(), helloName);
//创建一个HTMLAdaptoServer适配器,页面通过地址localhost:9092来访问JMX agent
ObjectName adaterName =new ObjectName("HelloMbean:name=htmladater,port=9092");
HtmlAdaptorServer adapter = new HtmlAdaptorServer();
adapter.start();
server.registerMBean(adapter, adaterName);
//创建服务JMXConnectorServer,是一个连接器服务器,被注册到MBeanServer上
//可以监听到客户端的请求并建议一个连接
Registry registry = LocateRegistry.createRegistry(9092);
JMXServiceURL jmxServerUrl = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9092/jmxrmi");
JMXConnectorServer jmxcs = JMXConnectorServerFactory.newJMXConnectorServer(jmxServerUrl,null, server);
jmxcs.start();
}
}
5. 访问MBean接口
方式一:使用JMXConnector来跟服务端的JMXConnector连接.
通过JMXConnector可以获取一个MBeanServerConnection,此接口可以和JMX代理中的MBeanServer通信。当然JMX代理公开了MBean接口中的属性和方法,可对属性和方法进行操作。如本例子通过MBeanServerConnection
来设置greeting属性或者通过MBeanServerInvocationHandler.newProxyInstance()
方法、JMX.newMBeanProxy()
方法生成一个HelloMBean的代理来访问属性和方法。
public class JMXClient {
public static void main(String[] args) throws Exception {
// Create an RMI connector client and
// connect it to the RMI connector server
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9092/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url,null);
// Get an MBeanServerConnection
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
ObjectName mbeanName = new ObjectName("HelloMBean:name=Hello");
//Get domains from MBeanServer
System.out.println("-------------Domains---------------");
String[] domains = mbsc.getDomains();
for(int i= 0 ;i < domains.length;i++) {
System.out.println("\tDomain["+i+"] = "+domains[i]);
}
// Get MBeanServer's default domain
System.out.println("\n-------------MBeanInfo---------------");
System.out.println("MBeanServer default domain = " + mbsc.getDefaultDomain());
// Get MBean count
System.out.println("MBean count = "+mbsc.getMBeanCount());
mbsc.setAttribute(mbeanName, new Attribute("Greeting","good luck"));
System.out.println(mbsc.getAttribute(mbeanName, "Greeting"));
//via proxy
//HelloMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, HelloMBean.class, true);
HelloMBean proxy = MBeanServerInvocationHandler.newProxyInstance(mbsc, mbeanName, HelloMBean.class, false);
mbeanProxy.setGreeting("Hello World!!!");
mbeanProxy.printGreeting();
mbeanProxy.printGreeting("DreamTech");
//via rmi
mbsc.invoke(mbeanName, "printGreeting", null, null);
mbsc.invoke(mbeanName,"printGreeting", new String[] {"DreamTech"}, new String[] {String.class.getName()});
//getMBeaninformation
MBeanInfo info = mbsc.getMBeanInfo(mbeanName);
System.out.println("Hello class-----"+info.getClassName());
for(int i =0 ; i <info.getAttributes().length;i++) {
System.out.println("Hello Attribute-----"+info.getAttributes()[i].getName());
}
for(int i =0;i< info.getOperations().length;i++) {
System.out.println("Hello Operation-----"+info.getOperations()[i].getName());
}
//All ObjectName of MBean
System.out.println("\n------------Query MBeanServer MBeans---------");
Set<ObjectInstance> set = mbsc.queryMBeans(null, null);
for(Iterator<ObjectInstance> it = set.iterator();it.hasNext();) {
ObjectInstance oi = it.next();
System.out.println(oi.getObjectName()+"\t");
}
jmxc.close();
}
}
运行结果:
-------------Domains---------------
Domain[0] = JMImplementation
Domain[1] = java.util.logging
Domain[2] = java.lang
Domain[3] = com.sun.management
Domain[4] = HelloMBean
Domain[5] = HelloMbean
Domain[6] = java.nio
-------------MBeanInfo---------------
MBeanServer default domain = DefaultDomain
MBean count = 24
good luck
Hello class-----com.ch01.Hello
Hello Attribute-----Greeting
Hello Operation-----printGreeting
Hello Operation-----printGreeting
------------Query MBeanServer MBeans---------
java.lang:type=OperatingSystem
java.lang:type=MemoryManager,name=Metaspace Manager
java.lang:type=MemoryPool,name=Metaspace
JMImplementation:type=MBeanServerDelegate
java.lang:type=MemoryPool,name=PS Old Gen
java.lang:type=ClassLoading
java.lang:type=Runtime
java.lang:type=GarbageCollector,name=PS Scavenge
com.sun.management:type=HotSpotDiagnostic
java.lang:type=Threading
java.lang:type=MemoryManager,name=CodeCacheManager
java.lang:type=MemoryPool,name=PS Eden Space
java.nio:type=BufferPool,name=mapped
java.lang:type=MemoryPool,name=Code Cache
java.lang:type=MemoryPool,name=Compressed Class Space
java.nio:type=BufferPool,name=direct
HelloMBean:name=Hello
java.lang:type=MemoryPool,name=PS Survivor Space
java.util.logging:type=Logging
com.sun.management:type=DiagnosticCommand
java.lang:type=GarbageCollector,name=PS MarkSweep
java.lang:type=Memory
HelloMbean:name=htmladater,port=9092
java.lang:type=Compilation
方式二:JMX代理中配置一个HtmlAdaptorServer
适配器,因此可以通过页面来访问MBean,地址为:localhost:9092
,通过页面可以对MBean接口和属性进行操作。
方式三:使用JDK提供的jconsole工具来访问,jconsole工具位置在D:\jdk1.8\bin\jconsole.exe
。jconsole提供了两种方式,如果管理程序在本地,可以直接通过本地进程来访问。如果在不在本地机器上,可以通过远程进程来进行连接。
6.参考
1.JDK官方发文档
2.《JMX in Action》
3.https://blog.youkuaiyun.com/icu/article/details/1795351