zoukankan      html  css  js  c++  java
  • Java Management Extensions (JMX)

    Java Management Extensions (JMX) - Best Practices

    注意,本文一些管理可能与JMX API或JSR 77 (J2EE management)不一致。它们都是在这些API定义之后才出现的经验教训。

    Object Names

    每个JMX MBean都有一个Object Name。

    Object Name 语法

    Object Name是 javax.management.ObjectName 的实例。

    Object Name可以是MBean的名字,可以是一种pattern - 用于匹配很多MBean的名字。

    格式:

    domain: key-property-list

    例子:

    com.sun.someapp:type=Whatsit,name=25

    在这里, domain是任意字符串。如果domain为空,会使用 使用Object Name的MBean Server的default domain。

    key-property-list,是一个MBean的name,包含一个或多个key properties。一个key property是这样的:name=value,例如 type=Thread。多个key properties以逗号间隔,如:type=Thread,name=DGC。

    注意

    • Object Name中的空格不被忽略。所以不要在符号(:或,)前后添加空格,否则会解释成带有空格的字符串。例如,type=Thread, name=DGC,这里后一个key就是" name",而非"name"!!!
    • Object Name是大小写敏感的。
    • 其key properties的顺序没有影响。
    • key的字符集是有限制的。建议严格遵守合法的Java identifier。-- 中文等字符会报错。
    • 建议value被引用起来(quoted),可以使用 ObjectName.quote。如果某个key的value是quoted,那它应该总是quoted。默认,如果value是一个字符串(而非数字),那它应该quoted,除非你确认它永远不会包含特殊字符。下面是几个例子:
      com.sun.someapp:type=Whatsit,name="25"
      com.sun.someapp:type=Whatsit,name="25,26" 
      简单的说,如果不是quoted,那第二个则是非法的。
    Object Name Conventions

    Object Name应该是predictable。就是说,如果你知道会有一个MBean来描述一个特定的对象,那你应该知道它的名字是什么。-- 简单的说,就是你得知道你们的命名规则,所以可以推出针对某个资源的MBean的Object Name。

    domain部分,应该以Java package name开头,后面可以(也可以不)跟随额外的文本。如下:

    com.sun.someapp:type=Whatsit,name=5
    com.sun.appserv.Domain1:type=Whatever 

    domain不应该包含斜杠/,这是MBean Server层级的保留字符。

    每个Object Name应该包含一个 "type="的key property。该property应该因object不同而不同(不能重复) -- 当然范围仅限于给定的domain。另外,JSR 77 定义了一个 "j2eeType=" 的key property,相同的意思。

    具有相同"type"的Object Name,应该拥有相同的key properties set,且语法和语义相同。

    其他最常见的key property就是"name"。"type=X,name=Y" 一般已足以命名一个MBean。

    有时候,定义额外的properties也是很有用的,可以利用patterns来搜索。例如,可以有"category"或"group" property:

    com.sun.someapp:group=configuration,*
    Object containment

    有时候,被管理的对象 逻辑上 被其他的被管理的对象 所包含。这样的对象不能独立存在。这种情况下,下面的schema通常是适合的。

    嘉定你有一个Server对象包含了Application对象,后者又包含了WebModule对象,WebModule对象又包含了Servlet对象。此时,它们的名字应该是这样的:

    domain:type=Server,name=server5
    domain:type=Server.Application,Server=server5,name=app1
    domain:type=Server.Application.WebModule,Server=server5,Application=app1,name=module3
    domain:type=Server.Application.WebModule.Servlet,Server=server5,Application=app1,WebModule=module3,name=default

    这里的"type" 使其简单易懂。

    146348.gif

    Standard MBean接口

    多数MBean都是使用Standard MBean接口来定义的。接口如下:

    public interface CacheControlMBean {
                                
        int getSize() throws IOException;            
        void setSize(int size) throws IOException;          
    
        int getUsage() throws IOException;
    
        int dropOldest(int n) throws IOException;
    }

    强烈建议 在可以的时候使用这种方式。这样可以更好的做文档,且允许client code通过代理(-使用 MBeanServerInvocationHandler )直接与其交互。

    不适用proxy的代码:

    MBeanServer mbs = ...;
                                
    Integer sizeI = (Integer) mbs.getAttribute(objectName, "Size");
    int size = sizeI.intValue();
                                
    if (size > desiredSize) {
        mbs.invoke(objectName,
                   "dropOldest",
                   new Integer[] {new Integer(size - desiredSize)},
                   new String[] {"int"});// 需要指明每个参数的类型
    }

    使用proxy的代码:

    MBeanServer mbs = ...;
    CacheControlMBean cacheControl = (CacheControlMBean)
        MBeanServerInvocationHandler.newProxyInstance(mbs,
                                                      objectName,
                                                      CacheControlMBean.class,
                                                      false);
                                
    int size = cacheControl.getSize();
    if (size > desiredSize)
        cacheControl.dropOldest(size - desiredSize);

    创建proxy的过程很复杂,但一旦创建完成,就可以像本地object一样来访问MBean。简单,且不容易出错。

    一个良好的实践是,Standard MBean接口 中的每个方法都抛出IOException (如上面的示例)。这强制你在使用proxy的代码中直接处理该异常。否则,(有问题的话)会出现通信问题,UndeclaredThrowableException,封装了原有的IOException。当然,如果你的MBean只是本地访问一下(就是同一个JVM中),那就没有必要抛出IOException了。一种简化本地访问的方式是:声明一个子类,不跑出IOException即可。

    public interface LocalCacheControlMBean extends CacheControlMBean {
                                
        int getSize();
        void setSize(int size);                    
    
        int getUsage();
        int dropOldest(int n);
    }

    这是JMX API自己采用的方式,用于远程接口MBeanServerConnection和本地接口MBeanServer之间。实现了该接口的Java类,可以实现LocalCacheControlMBean,比CacheControlMBean更好 -- 这有助于防止这两个接口跳出同步。

    Dynamic MBeans

    有时候,光有Standard MBean是不够的。当编译期间无法知道被管理的对象的接口时,就可以使用Dynamic MBeans。例如,你可以使用XML配置的MBean。每个配置项都被反射成MBean的attribute。MBean的attributes,是在runtime通过检查文件构建的!

    如果在编译期间就已经知道了MBean的管理接口,那通常没有必要实现DynamicMBean接口。如果你需要Dynamic MBean的一些特别能力,例如,提供attribute或operation的描述,或者禁用特定attribute或operation,那你应该考虑继承javax.management.StandardMBean,而非实现DynamicMBean接口。

    Model MBeans

    Model MBeans是JMX API的相当先进的特性。它可以生成一整套MBeans,而无需使用Model MBeans。Model MBeans很难编写,所以最好是有明显的好处,否则不要使用它。

    >>>>>>>>>>>>所以这里就略过好了<<<<<<<<<<<<<<<<

    将MBeans连接到resources

    MBean不仅是一个接口。肯定还有某种行为(some behavior)关联到那个接口。大多数时候,该行为依赖于被管理或监控的应用或资源。换句话说,MBean必须与其他Java objects交互!

    做到这点的最简单的方式是:将应用中已有的Java object放入MBean。例如,你可以将Cache object放入一个CacheMBean中。然后,该object既是应用的一部分,也是一个可以被远程访问的管理点。

    从OOP观点来说,最好不要让一个对象负责两种不同的事情。这里,Cache object既是一个cache,也是cache的管理,与该准则相悖。

    另一种方法就是让MBean是一个独立的object,然后拥有应用中object的引用即可。如上所述,Model MBeans提供了一种方式来做到这点。另一种方式就是编写一个Standard MBean:

    public class Cache { // the original application object
    
        //...
                                
        public int getSize() {...}
                                
        //...
    }
                                
    
                                 
    
    public class CacheManager implements CacheManagerMBean {
                                
        private final Cache cache;
    
        public CacheManager(Cache cache) { //hold a ref of the app's object 
            this.cache = cache;
        }
                                
        public int getSize() {
            return cache.getSize();
        }
    
        //...
    }
                                
    
                                 
    
    public interface CacheManagerMBean {
                                
        public int getSize();
        
        //...
    }
    MBean中的数据类型

    每个attribute 都有一种类型。在Standard MBean中的getter/setter。

    每个operation 都有一个返回类型,以及其每个参数的类型。在Standard MBean中,这些类型与相应的Java方法的返回和参数类型一致。

    getAttribute / setAttribute / invoke 都可能抛出异常。在Standard MBean中,这些异常来自getter / setter / operation method。

    通知,可能是javax.management.Notification的任意子类型。另外,其 userData字段 可能引用任意引用类型的对象。

    复杂数据类型

    经常需要复杂的数据类型,而非简单的Java int 或 String等类型。特别的,当attribute代表了某个value(如线程的状态)的snapshot时,只看一组不同的数值是没有意义的。

    使用多个attribute,而非复杂数据类型

    It is sometimes possible to address situations like this by breaking out the set of values into separate attributes and stipulating that those attributes must be read or written with a single getAttributes or setAttributes operation. But this tends to be a special-case solution that is difficult to implement and error-prone when clients fail to respect the requirement for using a single operation. It is also fragile in the face of evolution of the model. For example it does not extend to operation parameters or return values, and it does not work well if the values broken out into attributes are themselves complex types. So in general it is better to have a single data type representing the atomic set of values.

    使用model中特定的class

    Complex data types can also be represented by defining model-specific types, for example a ThreadInfo type for a snapshot of a thread. But this solution presents its own problems. Chief among these is that the client must have the same Java classes available. This is very inconvenient for model-neutral clients such as generic JMX consoles. It also potentially causes problems for access from languages other than Java (for instance, from scripts). And it can lead to serious class-loading headaches if the same client must interact with servers that have different versions of the model-specific classes. Finally, using arbitrary Java types can lead to unforeseen problems with serialization, since when an object is serialized other objects that it references may also be serialized, meaning that a client must have the classes for those other objects available too.

    自动下载class

    RMI can be configured to automatically download classes from server to client, or more rarely from client to server, that are not already present. Jini is fundamentally based on this idea, for example. However, this approach is generally not recommended for JMX-based management for a number of reasons:

    • It only works with the RMI connector defined by the JMX Remote API, not with the JMXMP connector defined there or with other currently non-standard connectors such as HTTP connectors.
    • It requires client and server to have access to a common code source. Typically, this is an HTTP server where the classes are installed. Setting up and maintaining this server is non-trivial.
    • The client (or server if it is the server that is downloading) must be configured with a security manager and it must be adjusted with exactly the right permissions. Getting the permissions wrong may mean that the downloaded code will not work, or may expose the client to security attacks.
    • Third parties are generally suspicious of code downloading. If the JMX model is exposed as a public interface, and if people are told that to use it they must configure their security to allow code downloading, this will meet with resistance.
    • The approach does not map well to access from non-Java clients such as scripts.
    • The approach typically requires client and server to share at least a Java interface describing the data type, so what is downloaded is an implementation of this interface. In the cases we are talking about, the classes are value classes and there is little to be gained by this separation of interface and implementation. Furthermore, the interface classes still have to be shared between client and server, with the problems already discussed above.
    Open MBeans (略)
    MBean接口的演变

    如果你的API包含了一个Standard MBean接口,那你必须指定是否允许用户编写其实现类。

    如果允许,那对该接口的任何修改都可能危害这些实现类。因此,最安全的方式就是,强调该接口仅用于读写MBean的attributes、调用其operations,或者利用代理创建(例如使用MBeanServerInvocationHandler)。

    否则,你需要使用子接口,然后原有的接口就无用了。如下:

    public interface CacheControl2MBean extends CacheControlMBean {
        void saveConfiguration() throws IOException;
    }
    
    
    

    如果你指定了用户不能实现你的接口,那你对该接口所做的修改 本质上:

    • 增加一个新的operation。
    • 重载一个现有的operation(不推荐重载)。
    • 更改write-only attribute的类型 - 从类或者接口 改成 父类或父接口(如果HashMap -> Map),或者从基本类型变成封装类型(如int -> Integer)。
    • 更改read-only attribute类型、operation的返回类型 - 从类改成子类(如Object -> String)。
    • 在throws语句中添加或删除unchecked exceptions。

    你不能做下面的这些,因为每种都会破坏通过MBeanServer访问MBean的方法,或者会破坏通过动态代理(使用MBeanServerInvocationHandler)访问MBean的代码的编译或执行:

    • 移除一个attribute或operation。
    • 更改write-only attribute的类型(不是上面提到的那种形式)。
    • 更改read-only attribute的类型或operation的返回类型(不是上提到的那种形式)。
    • 更改read-write attribute的类型。
    • 更改operation的参数。
    • 增加 或 移除 throws语句中的 checked exceptions (包括使用子类或父类替换)。

    如果你的MBean是Dynamic MBean,且没有Standard MBean接口(client用来创建dynamic proxy),那上面的限制可以稍稍放宽,允许以下:

    • changing the type of a write-only attribute in any way, provided that your MBean can still deal with the old type
    • changing the type of a read-write attribute from a class to a subclass, again provided that your MBean can still deal with the old type when the attribute is set
    • changing the parameters of an operation, provided that your MBean can still deal with the old parameters
    • there is no such thing as a throws clause in this case so the restriction on that is irrelevant.

    然而,强烈建议在可以的时候提供Standard MBean接口,因为后续使用的增加(什么鬼?)。

    复杂类型的演变(略)
    Notifications 通知

    Source 源

    虽然JMX API 允许任意对象作为通知的source 字段,但实践表明,最好是下面之一:

    • 一个ObjectName,通常是构建该Notifcation object的MBean的ObjectName。
    • 发送该通知的MBean的引用。

    当MBean Server将要转发一个通知时,如果其source为发送该通知的MBean的引用,那该Server会将source转成MBean的ObjectName。在这种情况下,如果通知被转发到另一个MBean,然后由那个MBean重新发送,此时MBean Server不会重写该source。因此,依赖这种重写是不推荐的。

    投递通知(notification delivery)的语义

    当定义在一个model中如何使用通知时,必须意识到传递通知的语义。remote clients不能假定它们会接收到它们监听的所有通知。JMX Remote API仅仅保证一种更弱的情况:

    A client either receives all notifications for which it is listening, or can discover that notifications may have been lost.

    A consequence of this weaker guarantee is that notifications should never be used to deliver information that is not also available in another way. The typical client observes the initial state of the information model, then reacts to changes in the model signalled by notifications. If it sees that notifications may have been lost, it goes back and observes the state of the model again using the same logic as it used initially. The information model must be designed so that this is always possible. Losing a notification must not mean losing information irretrievably.

    由此导致的情况就是,永远不要使用notifications来投递信息 - 如果其他方式也不可靠的话。典型的client观察information model的初始化状态,然后对model中的修改(-由通知发出的)做出应对。如果它发现通知可能丢失了,它会回去并再次观察model的状态 - 使用相同的逻辑。该information model必须被设计一下,这样才总是可用。丢失notification 必须 不能意味着 丢失information。(乱七八糟的)

    When a notification signals an event that might require intervention from the client, the client should be able to retrieve the information needed to react. This might be an attribute in an MBean that contains the same information as was included in the notification. If the information is just that a certain event occurred, it is often enough just to have a counter of how many times it occurred. Then a client can detect that the event occurred just by seeing that the counter has changed.

    当通知发出信号表明有一个事件(可能需要来自client的干涉)时,client应该能够获取需要应对的信息。这可能是MBean中的一个attribute - 包含了与通知中相同的信息。如果该信息仅仅是一个发生了的事件,那一个计数器足以。那client可以探测到事件的发生 - 只需要看到计数器改变即可。

    A client can discover when notifications are lost by registering a listener usingJMXConnector.addConnectionNotificationListener .

    client可以发现丢失了通知,只要使用JMXConnector.addConnectionNotificationListener注册一个listener即可。

    Large data values

    The existing JMX protocols are not very well adapted to moving about large data values (on the order of tens of thousands of bytes or more). This is true for notification payloads as well as for attribute values being read or written and for parameters and return values of operations. Although large data values will work, there may be an effect on concurrent access using the same connector client. It is often preferable to send a URL that allows the other end (client or server) to access the large data value directly.

    This is particularly important for notifications. Because of the way notification sending works in the current protocols, a notification may remain in the server for an indefinite period after being sent. Thus, if many large notifications are sent (for example because they have a very big string or byte array in theuserData field), an OutOfMemoryError may result.

    Related documentationundefined

    The article Apply JMX Best Practices from the Java Pro journal provides a set of higher-level recommendations that complement the ones in this document.

    地址:http://www.oracle.com/technetwork/java/javase/tech/best-practices-jsp-136021.html

  • 相关阅读:
    工作记录(1)- js问题
    QComboBox样式
    VLC的一些接口
    Qt使用std::sort进行排序
    如何正确使用QThread
    QChartView绘制饼状图
    libcurl编译使用,实现ftp功能
    QSplineSeries QChartView绘制曲线
    QLineSeries QChartView 生成折线
    QChart绘制折线区域
  • 原文地址:https://www.cnblogs.com/larryzeal/p/7406409.html
Copyright © 2011-2022 走看看