JMX学习总结
前段时间研究tomcat,发现里面涉及到了好多JMX的内容,那么抽点时间学习研究了一下,总结如下:
1.JMX是什么?
JMX(java ManagementExtensions)java管理扩展,“扩展”顾名思义就是不属于JDK的一部分,只是JDK的扩展。不过这个概念有点过时,JDK1.5以后的版本实现了默认的JMX引擎。也就是现在的JMX已经属于JDK的不可分割的一部分了。Tomcat中应用的JMX如果不额外配置的话,就采用了平台默认的MBeanServer :com.sun.jmx.mbeanserver.JmxMBeanServer.
2.JMX可以干什么?
JMX可以在运行期动态的配置和管理资源,说白了就是不需要重启服务器就可以实现配置的变更。另外一个重要的点就是通过JMX可以动态的跟踪我们的资源的信息。比如要跟踪JVM的内存,线程,类加载状况,cpu使用情况等.现在JMX已经广泛应用于tomcat等诸多服务器软件当中。 Java的自带的Jconsole客户端程序就是一个监控JVM信息的JMX客户端工具。
3.如何将我们的Bean交给JMX管理。
这里不说JMX的体系结构,这些原理性的东西网上一找一堆.我们按照JMX的编写要求来开发几个DEMO(其实也是一找一堆,我找了两个典型的)。
若要使一个java对象成为JMX管理的资源,则必须按照规范创建一些MBean,JMX的MBean类型分为4种:标准类型,动态类型,开放类型,模型类型。创建完MBean将其注册到MBean Server。 本次只研究一下实现最简单的标准MBean和在tomcat中应用的模型MBean,其他的两种MBean用的不多先不研究。
标准MBean的一个DEMO:
Car.java:
public class Car implements CarMBean{
private String color = "red";
public String getColor() {
return color;
}
public void setColor(String color){
this.color = color;
}
public void drive() {
System.out.println("Baby you can drive my car");
}
}
CarMBean.java:
//标准MBean
public interfaceCarMBean {
public String getColor();
public void setColor(String color);
public void drive();
}
StandardAgent.java:
public classStandardAgent {
private MBeanServer mBeanServer = null;
public StandardAgent(){
mBeanServer = MBeanServerFactory.createMBeanServer();
}
public MBeanServer getMBeanServer(){
return mBeanServer;
}
public ObjectNamecreateObjectName(String name) {
ObjectNameobjectName = null;
try {
objectName= newObjectName(name);
}catch(MalformedObjectNameException e) {
e.printStackTrace();
}catch(NullPointerException e) {
e.printStackTrace();
}
return objectName;
}
private voidcreateStandardBean(ObjectName objectName,String managedResourceClassName){
try {
mBeanServer.createMBean(managedResourceClassName,objectName);
}catch(InstanceAlreadyExistsException e) {
e.printStackTrace();
}catch(NotCompliantMBeanException e) {
e.printStackTrace();
}catch(MBeanRegistrationException e) {
e.printStackTrace();
}catch(ReflectionException e) {
e.printStackTrace();
}
}
public static void main(String[] args){
StandardAgentagent = newStandardAgent();
MBeanServermBeanServer = agent.getMBeanServer();
Stringdomain = mBeanServer.getDefaultDomain();
StringmanagedResourceClassName = "standard.mbean.Car";
ObjectNameobjectName = agent.createObjectName(domain+":type="+managedResourceClassName);
agent.createStandardBean(objectName,managedResourceClassName);
//这里的Color开头字母必须是大写
AttributecolorAttribute = newAttribute("Color","blue");
try {
mBeanServer.setAttribute(objectName,colorAttribute);
System.out.println(mBeanServer.getAttribute(objectName,"Color"));
mBeanServer.invoke(objectName,"drive",null,null);
}catch(Exception e) {
e.printStackTrace();
}
}
}
运行一下发现还是挺有意思的,Car类已经听StandardAgent的指挥了,开放一个端口通过网页来指挥这辆Car其实更有意思。要通过网页访问这个资源的话,你可以利用com.sun.jdmk.comm.HtmlAdaptorServer
在上面的main函数中加上:
ObjectNameadapterName = newObjectName("CarAgent:name=htmladapter,port=8082");
HtmlAdaptorServeradapter = newHtmlAdaptorServer();
server.registerMBean(adapter,adapterName);
adapter.start();
通过8082端口就可以设置Car的属性了. Ok 继续…
模型MBean:
Car.java :
public classCar {
private String color = "red";
public Car(){
System.out.println("Car constructor");
}
public String getColor() {
return color;
}
public void setColor(String color){
this.color = color;
}
public void drive() {
System.out.println("Baby you can drive my car.");
}
}
Ps: 有没有发现下面这个Car和上面那个Car有什么区别? 没错下面这辆Car就是一个POJO 没有实现或继承任何父类或接口。而上面那个必须实现一个MBean接口,这个也就是标准MBean的一个致命缺陷,即我们要管理某个Bean资源需要更改代码,但是代码有时我们是改不了的。
ModleAgent.java:
public classModelAgent {
private Registry registry;
private MBeanServer mBeanServer;
public ModelAgent(){
try {
registry = createRegistry();
mBeanServer = Registry.getServer();
}catch(Exception e) {
e.printStackTrace(System.out);
System.exit(1);
}
}
public MBeanServergetMBeanServer(){
return mBeanServer;
}
public RegistrycreateRegistry(){
try {
URLurl = ModelAgent.class.getResource("/model/mbean/modeler/api/car-mbean-descriptor.xml");
InputStreamstream = url.openStream();
Registry.loadRegistry(stream);
stream.close();
return Registry.getRegistry();
}catch(Exception e) {
e.printStackTrace();
}
return registry;
}
public ModelMBeancreateModelMBean(String mBeanName) throws Exception{
ManagedBeanmanaged = registry.findManagedBean(mBeanName);
if(managed == null){
System.out.println("ManagedBean null");
return null;
}
ModelMBeanmbean = managed.createMBean();
ObjectNameobjectName = createObjectName();
return mbean;
}
private ObjectNamecreateObjectName(){
ObjectNameobjectName = null;
Stringdomain = mBeanServer.getDefaultDomain();
try {
objectName= newObjectName(domain+":type=myCar");
}catch(Exception e) {
e.printStackTrace();
}
return objectName;
}
public static void main(String[] args){
ModelAgentagent = newModelAgent();
MBeanServermBeanServer = agent.getMBeanServer();
Carcar = newCar();
System.out.println("Creating ObjectName");
ObjectNameobjectName = agent.createObjectName();
try {
ModelMBeanmodelMBean = agent.createModelMBean("myMBean");
modelMBean.setManagedResource(car,"ObjectReference");
mBeanServer.registerMBean(modelMBean,objectName);
}catch(Exception e) {
e.printStackTrace();
}
try {
Attributeattribute = newAttribute("Color","green");
mBeanServer.setAttribute(objectName,attribute);
Stringcolor = (String) mBeanServer.getAttribute(objectName, "Color");
System.out.println("Color:"+color);
attribute= newAttribute("Color","blue");
mBeanServer.setAttribute(objectName,attribute);
color= (String) mBeanServer.getAttribute(objectName, "Color");
System.out.println("Color:"+color);
mBeanServer.invoke(objectName,"drive",null,null);
}catch(Exception e) {
e.printStackTrace();
}
}
}
Car-mbean-descriptor.xml:
<mbeans-descriptors>
<mbean name="myMBean" className="javax.management.modelmbean.RequiredModelMBean"
description="The ModelMBeanthat manages our Car object"
type="model.mbean.modeler.api.Car">
<attribute name="Color" description="The carcolor" type="java.lang.String"/>
<operation name="drive" description="drivemethod" impact="ACTION"returnType="void">
<parameter name="driver" description="the driverparameter" type="java.lang.String"/>
</operation>
<operation name="setColor" description="The setColor" returnType="void">
<parameter name="color" description="the setvalue" type="java.lang.String"/>
</operation>
<operation name="getColor" description="The setColor" returnType="void"/>
</mbean>
</mbeans-descriptors>
模型MBean不需要被管理的Bean实现自己的接口,但是有一点必须要做到,就是必须让MBean知道被管理的Bean的特征,比如标准MBean是定义了和原类的一套接口来获取到原来类的行为的。则模型MBean则是通过在xml文件中将被托管的Bean的模型描述出来。这个描述文件就是上面的 那个xml文件。
4. tomcat中JMX的应用:
Tomcat中采用了上文描述的第二种模型MBean:我们可以先搜索一下相关的xml配置文件,看看里面对mbean的描述信息,截图如下:
我们可以看到好多的mbeans-descriptors.xml文件。为什么都叫这个名:其实它们分属不同的包下,并且命名Registry这个类中默认写死了:
Tomcat中定义的而资源是怎么注册到MBeanserver的呢?
记得上次分析tomcat启动过程的时候,在Bootstrap类中看到了如下代码埋点:
还记得Bootstrap包含了三个类加载器,在构造完每个类加载器的时候就直接注册到了MBeanServer当中,好多的资源组建都是这么注册进来的。 例如:
通过调试发现在没有配置MBeanServer的情况下,采用了平台默认的MBeanServer:com.sun.jmx.mbeanserver.JmxMBeanServer. 上面那个ManagementFactory其实维护了一个统一的单例的 MBeanServer,保证我们的资源都在一个MBeanServer当中。
可见这个工厂里维护一个静态的MBeanServer成员。在其他地方注册时候获取的同样是同一个MBeanServer资源。
除了在代码中主动的向MBeanServer服务器注册一些资源外,在tomcat启动的时候还会根据事件的触发去加载注册一些MBean资源,同理在关闭的时候也会卸载这些资源. 具体的实现是这样的:在tomcat的基础配置 server.xml中维护了这么个监听器:
看注释可以看出来,这两个监听器是专门用来为JMX提供支持:
这两个监听器主要是在启动、关闭事件发生是去触发注册MBean的操作。
5.监控指定tomcat服务器中JVM的信息:
因为tomcat支持了JMX的规范,所以我们就可以通过JConsole监控上面的JVM状态,首先在tomcat启动的时候设置启动参数如下:
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.ssl="false"
-Dcom.sun.management.jmxremote.authenticate="false"
然后在打开我们的JConsole:指定远程的机器地址和端口号
然后就连上来了。
6: SecurityManager :
SecurityManager和上面说的没什么关系了,但是在看tomcat源码的时候发现好多地方都涉及了这段代码:
这个是在干嘛呢?原来他在找看看是否有设置了安全管理器,其实java是自己内置了一个SecurityManager的,只是开关没有打开,在启动的时候默认加上这个参数-Djava.security.manager就打开了默认的SecurityManager,打开这个的话java在加载一些class的时候就会查找一个授权文件,看看是否允许:这个授权文件的路径是:jdk1.6.0_10\jre\lib\security\java.policy
里面定义类似上图。当然我们也可以自己定义SecurityManager,自己定义SecurityManager的话只需要继承一个SecurityManager类然在代码中设置一下即可:
类签名:
SecurityManager就不多说了,必经主要先看下JMX嘛!
Ps:以上就是从应用层面学习了一下JMX,本来是想从源码层面解读一下,发现这里面的东西还是挺复杂的,有时间在读它的源码了。还有一个问题抛出来,本来是想用本机的jconsole连接一下daily服务器的,发现已经配置了jmx远程连接端口,但是这个端口用jconsole连接不上(当然线上不能随便连接哈)。有点想不明白,为什么呢,ping都ping不通,不知为什么?有知道的请多多指教吧。