【问题标题】:Same JMX Mbean class for many application at same server同一服务器上许多应用程序的相同 JMX Mbean 类
【发布时间】:2011-08-16 07:44:44
【问题描述】:

我有超过 5 个 Spring Web 应用程序,它们都在使用另一个公共库。这个公共库有它自己的 MBean。由于强制唯一的 objectName 约束,我的应用程序无法部署在同一台服务器上。

我使用MBean的方式是这样的:

@ManagedResource(objectName = "com.org.city:name=City", description = "City related operations")

我想为所有应用程序使用具有不同 objectNames 的相同 MBean 类。在不复制我的 MBean 的情况下使用它的正确方法是什么。

谢谢

【问题讨论】:

    标签: java spring jmx mbeans


    【解决方案1】:

    我遇到了同样的问题,并且是基于 Cemo's solution 构建的。这是一个示例实现。

    context.xml

    <!-- Set up jmx bean auto scanning -->
    <!-- Note: we're not using <context:mbean-export /> because we need to provide our own naming strategy -->
    <bean id="mbeanExporter" class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter">
        <property name="namingStrategy">
            <bean class="com.foo.MultiAppMetadataNamingStrategy">
                <property name="applicationName" value="${application.name}" />
            </bean>
        </property>
    </bean>
    

    MultiAppMetadataNamingStrategy.java

    public class MultiAppMetadataNamingStrategy implements ObjectNamingStrategy, InitializingBean {
    
        private String applicationName;
    
        public MultiAppMetadataNamingStrategy() {
        }
    
        public MultiAppMetadataNamingStrategy(String applicationName) {
            this.applicationName = Preconditions.checkNotNull(applicationName, "applicationName must not be null");
        }
    
        public void setApplicationName(String applicationName) {
            this.applicationName = Preconditions.checkNotNull(applicationName, "applicationName must not be null");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            if (applicationName == null) {
                throw new IllegalArgumentException("Property 'applicationName' is required");
            }
        }
    
        @Override
        public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
            Class managedClass = AopUtils.getTargetClass(managedBean);
            String domain = ClassUtils.getPackageName(managedClass);
    
            Hashtable<String, String> properties = new Hashtable<>();
            properties.put("type", ClassUtils.getShortName(managedClass));
            properties.put("name", beanKey);
            // ensure the application name is included as a property in the object name
            properties.put("app", applicationName);
            return ObjectNameManager.getInstance(domain, properties);
        }
    }
    

    这允许像这样设置一个 mbean:

    package com.foo;
    
    @ManagedResource(description = "Bean description")
    public class MyBean {
        ...
    }
    

    这将注册一个对象名称为com.foo:name=myBean,type=MyBean,app=CustomAppName的mbean

    【讨论】:

      【解决方案2】:

      我已经为自定义行为实现了 ObjectNamingStrategy。

         @Override
        public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
           Class managedClass = AopUtils.getTargetClass(managedBean);
           Hashtable<String, String> properties = new Hashtable<String, String>();
           properties.put("type",ClassUtils.getPackageName(managedClass).concat(".").concat(ClassUtils.getShortName(managedClass)));
           properties.put("name", beanKey);
           return ObjectNameManager.getInstance(domain, properties);
        }
      

      【讨论】:

        【解决方案3】:

        你需要更改mbean导出器的registering behavoiur

        <property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>
        

        但这仍然意味着只有一个应用程序注册 bean。从逻辑上讲,您不能在多个应用程序中拥有多个具有相同名称的 mbean。如何确定调用哪个应用程序?使用应用程序名称作为 mbean 名称的前缀。

        【讨论】:

        • 我想对所有应用程序使用相同的 mbean。在这种情况下,它将用于刚刚部署的应用程序?
        • 你不能。使用不同的名称注册(使用某些版本)。否则它怎么知道调用哪个应用程序?
        • 是的,我也有同样的顾虑,但我不知道如何使用不同的名称注册。有没有办法根据父应用程序参数化这些objectName?
        • 您应该能够使用 SPeL 作为名称,以及一些外部化的配置属性。例如app.name,名称为${app.name}SomeManagedBean
        • 但我正在使用注解,它们是使用类型级别的。带有类型级别注释的 Spel 的正确用法是什么?
        【解决方案4】:

        您可以使用占位符根据属性定义简单的命名策略。
        每场战争都会有它自己的 app.properties 副本
        例如

        带有属性文件:app.properties

        appName=MyApp1 #Every app will have it own value e.g,MyApp2,MyApp3,MyApp4,MyApp5
        

        和一个 PropertiesPlaceHolder

        <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
             <property name="placeholderPrefix" value="$app{" />    
             <property name="location" value="classpath:app.properties"/>
        </bean>
        

        定义对象名称

        @ManagedResource(objectName=com.mycompany:name=$app{appName}-MyBean")
        public class MyBean {}
        

        你的 bean 将被命名

        com.mycompany
            +MyApp1-MyBean
            +MyApp2-MyBean
            +MyApp3-MyBean
            +MyApp4-MyBean
            +MyApp5-MyBean
        

        您可以使用多个单一属性占位符。
        适用于 Spring 4.0.2

        【讨论】:

          【解决方案5】:

          这些答案帮助我指出了正确的方向,但是基于注释的设置缺少一些部分(但是我没有使用 Spring Boot)

          Spring Docs on this subject 说:

          如果您更喜欢使用基于注解的方法来定义您的管理接口,则可以使用 MBeanExporter 的便利子类:AnnotationMBeanExporter。定义此子类的实例时,不再需要命名策略、汇编器和属性源配置,因为它将始终使用标准的基于 Java 注释的元数据(也始终启用自动检测)。实际上,@EnableMBeanExport @Configuration 注解支持更简单的语法,而不是定义 MBeanExporter bean。

          但是使用@EnableMBeanExport 会阻止您定义自己的NamingStrategy

          因此,不要仅仅设置一个 @Bean 方法,该方法返回我的 MBeanExporter,并使用使用上下文路径的自定义命名策略。

          @Configuration
          public class JmxUtil {
          
              @Value("#{servletContext.contextPath}")
              private String contextPath;
              private String domain = "foo.bar";
          
              @Bean
              public MBeanExporter mbeanExporter() {
                  AnnotationMBeanExporter exporter = new AnnotationMBeanExporter();
                  exporter.setNamingStrategy((managedBean, beanKey) -> {
                      return ObjectNameManager.getInstance(domain, new Hashtable<>(ImmutableMap.of(
                          "name", beanKey,
                          "instance", contextPath
                      )));
                  });
                  exporter.setDefaultDomain(domain);
                  return exporter;
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-05-03
            • 1970-01-01
            • 1970-01-01
            • 2015-07-12
            • 2015-02-18
            • 2019-10-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多