在同一服务器上为多个应用程序使用相同的JMX Mbean类

5

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

我使用MBeans的方式如下:

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

我希望能够在所有应用程序中使用相同的MBean类,并使用不同的objectNames。正确的利用方式是什么,以避免复制我的MBeans。
谢谢。
5个回答

5

我遇到了同样的问题,并基于Cemo的解决方案进行了改进。这里是一个示例实现。

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。


4

我已经实现了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);
  }

0

您需要更改MBean导出器的注册行为

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

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


我想在所有应用程序中使用相同的MBean。在这种情况下,它将仅用于最后部署的应用程序? - Cemo
你不能这样做。请使用不同的名称进行注册(使用某个版本)。否则它将不知道调用哪个应用程序。 - Bozho
但是我正在使用注释,并且它们被用于类型级别。在使用类型级别的注释时,Spel的正确用法是什么? - Cemo
嗯,尝试在注释中使用 spel - 在许多情况下都有效。@...(name="${prop}FooBar") - Bozho
我开始感觉到我缺少了什么。这个表达式还没有被评估。请注意,我正在使用ManagedResource注释而不是Value。 - Cemo
显示剩余3条评论

0

这些答案帮助我找到了正确的方向,但是在基于注释的设置中还缺少一些要素(我没有使用Spring Boot)

Spring文档上说:

如果您喜欢使用基于注释的方法来定义管理接口,则可以使用AnnotationMBeanExporter作为MBeanExporter的子类。当定义此子类的实例时,不再需要namingStrategy、assembler和attributeSource配置,因为它将始终使用标准的基于Java注释的元数据(自动检测也始终启用)。事实上,与其定义一个MBeanExporter bean,@EnableMBeanExport @Configuration注释支持更简单的语法。

但是使用@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;
    }
}

0

您可以使用占位符定义基于属性的简单命名策略。
每个版本都会有自己的 app.properties 副本。
例如:

使用 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。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接