springboot情操陶冶-jmx解析

來源:互聯網
上載者:User

標籤:and   nap   hash   4.0   free   spring   trade   any   ppc   

承接前文springboot情操陶冶[email protected]註解解析,近期筆者接觸的項目中有使用到了jmx的協議架構,遂在前文的基礎上講解下springboot中是如何整合jmx的

知識儲備

JMX:Java Management Extension(Java管理應用擴充),這種機制可以方便的管理、監控正在啟動並執行Java程式。常用於監控管理線程、記憶體、日誌Level、服務重啟、系統內容等等。
更多的知識點參考此篇文獻:52800742。筆者此處引用其中的架構圖方便理解

JmxAutoConfiguration

springboot通過在META-INF\spring.factories檔案指定EnableAutoConfiguration屬性值為JmxAutoConfiguration,便基本搭建了jmx的架構模子。聽起來挺神奇的,筆者這就分析源碼來一窺究竟

註解

首先看下JmxAutoConfiguration頭上的註解

@Configuration@ConditionalOnClass({ MBeanExporter.class })@ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true", matchIfMissing = true)public class JmxAutoConfiguration implements EnvironmentAware, BeanFactoryAware {}

由上可知,要想使jmx環境生效,前提為

  • classpath環境得存在org.springframework.jmx.export.MBeanExporter

  • 環境變數spring.jmx.enabled設定為true,預設為true

一般引入springboot上述條件均是滿足的,只是使用者可通過spring.jmx.enabled屬性來開關啟jmx環境

@Bean方法

其下有三個方法,分別被@Bean@Conditional註解所修飾。筆者依次來進行解讀

JmxAutoConfiguration#objectNamingStrategy()-擷取ObjectName的建置原則

    @Bean    @ConditionalOnMissingBean(value = ObjectNamingStrategy.class, search = SearchStrategy.CURRENT)    public ParentAwareNamingStrategy objectNamingStrategy() {        // create namingStrategy        ParentAwareNamingStrategy namingStrategy = new ParentAwareNamingStrategy(                new AnnotationJmxAttributeSource());        // have a try to read environment property 'spring.jmx.default-domain'        String defaultDomain = this.environment.getProperty("spring.jmx.default-domain");        if (StringUtils.hasLength(defaultDomain)) {            namingStrategy.setDefaultDomain(defaultDomain);        }        return namingStrategy;    }

上述代碼也很簡單,其中環境變數spring.jmx.default-domain代表jmx預設的域掛載。

  • 如果@ManagedResource沒有指定objectName屬性或者beanName不符合jmx文法,則預設選取當前類的包名作為objectName

JmxAutoConfiguration#mbeanServer()-建立MBeanServer

    @Bean    @ConditionalOnMissingBean    public MBeanServer mbeanServer() {        // 1.first to search classpath exsit 'weblogic.management.Helper'/'com.ibm.websphere.management.AdminServiceFactory' class if or not          SpecificPlatform platform = SpecificPlatform.get();        if (platform != null) {            return platform.getMBeanServer();        }        // 2.via MBeanServerFactoryBean to create MBeanServer        MBeanServerFactoryBean factory = new MBeanServerFactoryBean();        factory.setLocateExistingServerIfPossible(true);        factory.afterPropertiesSet();        return factory.getObject();    }

筆者此處只關注MBeanServerFactoryBean是如何建立mbeanserver的,直接去看下其實現的afterPropertiesSet()方法

    @Override    public void afterPropertiesSet() throws MBeanServerNotFoundException {        // 1.嘗試去找尋已存在的mbeanserver        if (this.locateExistingServerIfPossible || this.agentId != null) {            try {                this.server = locateMBeanServer(this.agentId);            }            catch (MBeanServerNotFoundException ex) {                if (this.agentId != null) {                    throw ex;                }                logger.info("No existing MBeanServer found - creating new one");            }        }        // 2.如果上述不存在mbeanserver,則調用jmx api產生mbeanserver        if (this.server == null) {            this.server = createMBeanServer(this.defaultDomain, this.registerWithFactory);            this.newlyRegistered = this.registerWithFactory;        }    }

主要調用jmx api的MBeanServerFactory.createMBeanServer()方法建立mbeanserver,具體的建立過程筆者就不深究了,感興趣的讀者可自行分析

JmxAutoConfiguration#mbeanExporter()-建立mbeanExporter
源碼如下

    @Bean    @Primary    @ConditionalOnMissingBean(value = MBeanExporter.class, search = SearchStrategy.CURRENT)    public AnnotationMBeanExporter mbeanExporter(ObjectNamingStrategy namingStrategy) {        // 1.建立註解類型的AnnotationMBeanExporter,表明採取註解方式載入mbean        AnnotationMBeanExporter exporter = new AnnotationMBeanExporter();        exporter.setRegistrationPolicy(RegistrationPolicy.FAIL_ON_EXISTING);        // 2.set above namingStrategy        exporter.setNamingStrategy(namingStrategy);        // 3.set mbeanserver via spring applicationContext        String serverBean = this.environment.getProperty("spring.jmx.server",                "mbeanServer");        if (StringUtils.hasLength(serverBean)) {            exporter.setServer(this.beanFactory.getBean(serverBean, MBeanServer.class));        }        return exporter;    }

建立AnnotationMBeanExporter類來讀取註解方式的mbean,並優先從spring上下文讀取mbeanserver。

  • 環境變數spring.jmx.server如果沒有指定的話則預設讀取beanName為‘mbeanServer‘的MBeanServer對象,這與JmxAutoConfiguration#mbeanServer()方法註冊的bean不謀而合

通過上述的分析可得,筆者發現最終暴露給外界調用jmx協議是通過AnnotationMBeanExporter來完成的,其裡面也蘊含瞭解析mbean相關註解的玄機

AnnotationMBeanExporter

其實現的常用介面有InitializingBean/SmartInitializingSingleton/DisposableBean以及MBeanExportOperations

建構函式
    public AnnotationMBeanExporter() {        setNamingStrategy(this.metadataNamingStrategy);        setAssembler(this.metadataAssembler);        setAutodetectMode(AUTODETECT_ALL);    }

主要是設定基礎的屬性

afterPropertiesSet()

InitializingBean介面實作類別如下

    @Override    public void afterPropertiesSet() {        // have a try to find exsiting mbeanserver        if (this.server == null) {            this.server = JmxUtils.locateMBeanServer();        }    }
afterSingletonsInstantiated()

SmartInitializingSingleton介面實作類別如下

    @Override    public void afterSingletonsInstantiated() {        try {            logger.info("Registering beans for JMX exposure on startup");            registerBeans();            registerNotificationListeners();        }        catch (RuntimeException ex) {            // Unregister beans already registered by this exporter.            unregisterNotificationListeners();            unregisterBeans();            throw ex;        }    }

此處的registerBeans()方法便是mbeanserver去註冊mbean的過程,可以繼續跟蹤下

    protected void registerBeans() {        // The beans property may be null, for example if we are relying solely on autodetection.        if (this.beans == null) {            this.beans = new HashMap<>();            // Use AUTODETECT_ALL as default in no beans specified explicitly.            if (this.autodetectMode == null) {                this.autodetectMode = AUTODETECT_ALL;            }        }        // Perform autodetection, if desired.        int mode = (this.autodetectMode != null ? this.autodetectMode : AUTODETECT_NONE);        if (mode != AUTODETECT_NONE) {            if (this.beanFactory == null) {                throw new MBeanExportException("Cannot autodetect MBeans if not running in a BeanFactory");            }            if (mode == AUTODETECT_MBEAN || mode == AUTODETECT_ALL) {                // Autodetect any beans that are already MBeans.                logger.debug("Autodetecting user-defined JMX MBeans");                autodetect(this.beans, (beanClass, beanName) -> isMBean(beanClass));            }            // Allow the assembler a chance to vote for bean inclusion.            if ((mode == AUTODETECT_ASSEMBLER || mode == AUTODETECT_ALL) &&                    this.assembler instanceof AutodetectCapableMBeanInfoAssembler) {                autodetect(this.beans, ((AutodetectCapableMBeanInfoAssembler) this.assembler)::includeBean);            }        }        // mbeanserver register mbeans        if (!this.beans.isEmpty()) {            this.beans.forEach((beanName, instance) -> registerBeanNameOrInstance(instance, beanName));        }    }

避免代碼過長帶來的視覺疲勞,筆者此處對關鍵方法作下總結

  1. autodetect()方法的作用是遍曆bean工廠上的所有beanDefinition,找尋合格beans作為後續的mbeans註冊。找尋條件歸結如下
    • 攜帶@MBean註解的類
    • DynamicBean介面實作類別
    • *MBean介面的實作類別
    • 攜帶@ManagedResource註解的類
  2. registerBeanNameOrInstance()方法則會對合格beans進行mbean的註冊操作,操作步驟如下
    1. 根據類上的@ManagedResource註解的屬性objectName產生ObjectName對象
    2. 如果合格mbean是攜帶@ManagedResource註解的,則產生ModelBean對象並讀取@ManagedOperation@ManagedAttribute等jmx註解
    3. 最後註冊上述的mbean到mbeanserver上

通過上述的操作便可以將搜尋到的mbean註冊至mbeanserver上了,只要使用者使用@ManagedOperation@ManagedAttribute@ManagedResource註解搭配即可

附例

pom內容

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.example</groupId>    <artifactId>demo</artifactId>    <version>0.0.1-SNAPSHOT</version>    <packaging>jar</packaging>    <name>demo-springboot</name>    <description>Demo project for Spring Boot</description>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.0.3.RELEASE</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>        <java.version>1.8</java.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

mbean建立

package com.hundsun.quote.server.monitor;...import java.util.HashMap;import java.util.Map;/** * common monitor MBean for system and application * * @author nanco * @create 2018/8/6 **/@Configuration@ManagedResource(value = "monitor:name=SystemCommonMonitor")public class SysCommonMonitorMBean implements InitializingBean {    private static AbstractMonitorService commonMonitorService;    private Gson gson = new Gson();    @Autowired    private ApplicationContext springApplicationContext;    @Override    public void afterPropertiesSet() throws Exception {        Assert.notNull(springApplicationContext, "spring applicationContext is not autowired");        commonMonitorService = springApplicationContext.getAutowireCapableBeanFactory().createBean(CommonMonitorService.class);    }    @ManagedAttribute    public String getSystemCommonInfo() {        Map<String, String> systemMap = new HashMap<>(8);        systemMap.put("sysCpuRatio", commonMonitorService.getSysCpuRatio());        systemMap.put("sysCurrentTime", commonMonitorService.getSysCurrentTime());        systemMap.put("sysStartupTime", commonMonitorService.getSysStartupTime());        systemMap.put("sysMemoryRatio", commonMonitorService.getSysMemoryRatio());        systemMap.put("sysDiskTotal", commonMonitorService.getSysDiskTotal());        systemMap.put("sysDiskFree", commonMonitorService.getSysDiskFree());        return gson.toJson(systemMap);    }    @ManagedAttribute    public String getAppCommonInfo() {        Map<String, String> applicationMap = new HashMap<>(4);        applicationMap.put("appCpuRatio", commonMonitorService.getAppCpuRatio());        applicationMap.put("appMemoryRatio", commonMonitorService.getAppMemoryRatio());        applicationMap.put("appStartupTime", commonMonitorService.getAppStartupTime());        return gson.toJson(applicationMap);    }}
package com.jing.quote.server.monitor;....import org.springframework.context.annotation.Configuration;import org.springframework.jmx.export.annotation.ManagedOperation;import org.springframework.jmx.export.annotation.ManagedOperationParameter;import org.springframework.jmx.export.annotation.ManagedOperationParameters;import org.springframework.jmx.export.annotation.ManagedResource;import javax.annotation.Resource;import java.util.List;/** * trade date Mbean * * @author nanco * @create 2018/8/6 **/@Configuration@ManagedResource(value = "monitor:name=TradeDateMonitor")public class TradeDateMonitorMBean {    private static final String OK_RESP = "Execute Success!";    private static final String FAIL_RESP = "Execute Fail!";    private Gson gson = new Gson();    @Resource    private DsTradeDateService tradeDateService;    @Resource    private DsBaseService baseService;    @ManagedOperation(description = "select_all_trade_date_excl")    public String selectAllTradeDateExcl() {        List<TradeDateExcl> tradeDateExcls = tradeDateService.selectAllTradeDateExcl();        return gson.toJson(tradeDateExcls);    }    @ManagedOperation(description = "insert_trade_date_excl")    @ManagedOperationParameters(value = {            @ManagedOperationParameter(name = "financeMic", description = "financeMic"),            @ManagedOperationParameter(name = "exclDate", description = "exclDate"),            @ManagedOperationParameter(name = "dayOfWeek", description = "dayOfWeek"),            @ManagedOperationParameter(name = "exclDesc", description = "exclDesc")    })    public String insertTradeDateExcl(String financeMic, int exclDate, int dayOfWeek, String exclDesc) {        TradeDateExcl tradeDateExcl = new TradeDateExcl();        tradeDateExcl.setFinanceMic(financeMic);        tradeDateExcl.setExclDesc(exclDesc);        tradeDateExcl.setExclDate(exclDate);        tradeDateExcl.setDayOfWeek(dayOfWeek);        boolean executeFlag = tradeDateService.insertTradeDateExcl(tradeDateExcl);        if (executeFlag) {            return TradeDateMonitorMBean.OK_RESP;        } else {            return TradeDateMonitorMBean.FAIL_RESP;        }    }    @ManagedOperation(description = "delete_trade_date_excl")    @ManagedOperationParameters(value = {            @ManagedOperationParameter(name = "financeMic", description = "financeMic"),            @ManagedOperationParameter(name = "exclDate", description = "exclDate")    })    public String deleteTradeDateExcl(String financeMic, int exclDate) {        TradeDateExclKey exclKey = new TradeDateExclKey();        exclKey.setExclDate(exclDate);        exclKey.setFinanceMic(financeMic);        boolean executeFlag = tradeDateService.deleteTradeDateExcl(exclKey);        if (executeFlag) {            return TradeDateMonitorMBean.OK_RESP;        } else {            return TradeDateMonitorMBean.FAIL_RESP;        }    }}

jmx serviceUrl暴露

package com.jing.quote.server.config;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import org.springframework.jmx.support.ConnectorServerFactoryBean;import org.springframework.remoting.rmi.RmiRegistryFactoryBean;@Configurationpublic class JmxAutoConfiguration {    private static final Log LOGGER = LogFactory.getLog(JmxAutoConfiguration.class);    @Value("${jmx.rmi.host:localhost}")    private String rmiHost;    @Value("${jmx.rmi.port:9888}")    private Integer rmiPort;    @Bean    public RmiRegistryFactoryBean rmiRegistry() {        // 指定連接埠可以開放建立名字服務        final RmiRegistryFactoryBean rmiRegistryFactoryBean = new RmiRegistryFactoryBean();        rmiRegistryFactoryBean.setPort(rmiPort);        rmiRegistryFactoryBean.setAlwaysCreate(true);        LOGGER.info("RmiRegistryFactoryBean create success !!");        return rmiRegistryFactoryBean;    }    @Bean    @DependsOn("rmiRegistry")    public ConnectorServerFactoryBean connectorServerFactoryBean() throws Exception {        final ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();        connectorServerFactoryBean.setObjectName("connector:name=rmi");        connectorServerFactoryBean.setServiceUrl(                String.format("service:jmx:rmi://%s:%s/jndi/rmi://%s:%s/jmxrmi", rmiHost, rmiPort, rmiHost, rmiPort));        LOGGER.info("ConnectorServerFactoryBean create success !!");        return connectorServerFactoryBean;    }}

jconsole訪問,直接遠端連線至service:jmx:rmi://localhost:9888/jndi/rmi://localhost:9888/jmxrmi即可(預設)

結束語

讀者在閱讀本博文的時候,建議首先按照筆者上述給出的文獻連結查閱jmx相關知識點,再結合此文便會對springboot整合jmx架構有一定的瞭解

springboot情操陶冶-jmx解析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.