JMX之介紹和mac環(huán)境使用介紹

1.概念簡介

JMX(Java Management Extensions)是一個為應(yīng)用程序植入管理功能的框架。JMX是一套標(biāo)準(zhǔn)的代理和服務(wù),
實際上酿炸,用戶可以在任何Java應(yīng)用程序中使用這些代理和服務(wù)實現(xiàn)管理糙申。主要用于對JAVA應(yīng)用程序和JVM進(jìn)行監(jiān)控和管理。

JConsole和JVisualVM中能夠監(jiān)控到JAVA應(yīng)用程序和JVM的相關(guān)信息都是通過JMX實現(xiàn)的媒殉〉5校看下網(wǎng)上的一張結(jié)構(gòu)圖


jmx

三層結(jié)構(gòu),分別負(fù)責(zé)通信廷蓉,代理全封,管理資源bean马昙。

  • 最上的通信層:Agent如何被遠(yuǎn)端用戶訪問的細(xì)節(jié)。它定義了一系列用來訪問Agent的接口和組件刹悴,包括Adapter和Connector的描述行楞。

  • 中間的代理層:管理相應(yīng)的資源,并且為遠(yuǎn)端用戶提供訪問的接口土匀。Agent層構(gòu)建在Intrumentation層之上子房,并且使用并管理 Instrumentation層內(nèi)部描述的組件。Agent層主要定義了各種服務(wù)以及通信模型就轧。該層的核心是一MBeanServer,所有的MBean都要向它注冊证杭,才能被管理。注冊在MBeanServer上的MBean并不直接和遠(yuǎn)程應(yīng)用程序進(jìn)行通信妒御,他們通過協(xié)議適配器(Adapter)和連接器(Connector)進(jìn)行通信解愤。通常Agent由一個MBeanServer和多個系統(tǒng)服務(wù)組成隶垮。JMX Agent并不關(guān)心它所管理的資源是什么

  • 基礎(chǔ)管理bean:主要包括了一系列的接口定義和描述如何開發(fā)MBean的規(guī)范礼华。通常JMX所管理的資源有一個或多個MBean組成纺荧,因此這個資源可以是任何由Java語言開發(fā)的組件啡彬,或是一個JavaWrapper包裝的其他語言開發(fā)的資源枢纠。

2. 使用舉例

很多開源框架都會用到j(luò)mx監(jiān)控系統(tǒng)狀態(tài)钙姊,比如我們熟悉的tomcat送浊。下面舉個簡單的例子博秫,說明我們實際使用場景边灭。

  • 需求异希,做一個自定義的classloader類,進(jìn)行加載我指定包路徑下的類存筏,并管理加載了這些類:

  • MBean定義
    ??根據(jù)Mbean的基礎(chǔ)定義


    image.png

我們使用最基本的standard MBean定義接口宠互,此時有以下要求:

  1. 接口和實現(xiàn)類必須在同一包下
    2.接口名字為TestLoaderMBean,那么實現(xiàn)類必須叫TestLoader(MBean前字符串)椭坚,一個單詞不許錯予跌,否則會報錯

因此,我們接口定義

package jmx.beans;

public interface TestLoaderMBean {

    /**
     * 獲取loader名
     * @return
     */
    String loaderName();

    /**
     * 獲取loader總共加載的類數(shù)量
     * @return
     */
    int loaderClassSize();

    int getLoaderClassSize();


    /**
     * 獲取加載了的所有類名稱
     * @return
     */
    String loaderClassesNames();

    /**
     * 加載所有類
     */
    void loaderClasses();

    /**
     * 獲取用戶指定加載的包路徑
     * @return
     */
    String packageName();


    /**
     * 銷毀此加載器
     */
    void destory();
}

接口定義了loader的管理規(guī)范善茎,聲明了對應(yīng)此loader的管理工作券册,然后定義其實現(xiàn)類

package jmx.beans;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class TestLoader implements TestLoaderMBean {

    private String name;

    private int loaderClassSize;


    private List<String> loaderClasss;

    private String packageName;


    public TestLoader(String name, String packageName) {
        this.name = name;
        this.packageName = packageName;
    }

    public TestLoader( ) {
    }


    @Override
    public String loaderName() {
        return name;
    }

    @Override
    public int getLoaderClassSize() {
        return loaderClassSize;
    }

    @Override
    public int loaderClassSize() {
        return loaderClassSize;
    }

    @Override
    public String loaderClassesNames() {
        StringBuffer sb=new StringBuffer();
        if(loaderClasss!=null){
            loaderClasss.stream().forEach(s->{
                sb.append(s).append(";\n");
            });
        }
        return sb.toString();

    }


    @Override
    public String packageName() {
        return packageName;
    }

    public void setName(String name) {
        this.name = name;
    }


    public void setLoaderClasss(List<String> loaderClasss) {
        this.loaderClasss = loaderClasss;
        if (loaderClasss != null) {
            loaderClassSize = loaderClasss.size();
        }
    }


    @Override
    public void loaderClasses() {
        System.out.println("加載" + packageName + "所有類");
        Random random = new Random(3);
        int i = random.nextInt(3);
        try {
            TimeUnit.SECONDS.sleep(i);

            String class1 = "com.class1";
            String class2 = "com.class2";
            String class3 = "com.class3";
            if (loaderClasss == null) {
                loaderClasss = new ArrayList<>();
            }
            loaderClasss.add(class1);
            loaderClasss.add(class2);
            loaderClasss.add(class3);
            loaderClassSize = loaderClasss.size();
            System.out.println("size"+loaderClassSize);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void destory() {
        loaderClasss = null;
        loaderClassSize = 0;
        System.out.println("銷毀所有類");
    }
}

模擬類中,注意到getLoaderClassSize()和loaderClassSize()方法是重復(fù)的垂涯,實際上這樣是有區(qū)別的烁焙,一會我們會用到。
最后需要注冊一個代理服務(wù)

package jmx;


import jmx.beans.TestLoader;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;

public class LocalAgent {


    private MBeanServer mbs;

    public LocalAgent() throws Exception {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

        ObjectName mbeanName = new ObjectName("jmx:type=testLoader");

        TestLoader mbean = new TestLoader("test","com.class");

        mbs.registerMBean(mbean, mbeanName);

        Thread.sleep(Long.MAX_VALUE);


    }



    public static void main(String args[]) throws Exception {
        LocalAgent agent = new LocalAgent();
    }

}

這樣我們就是實現(xiàn)了簡單jmx的管理示例耕赘,當(dāng)然MBeanServer是支持rmi骄蝇、html等遠(yuǎn)程監(jiān)控協(xié)議,如rmi:

package jmx;

import jmx.beans.TestLoader;

import javax.management.*;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;

public class RmiAgent
{
    public RmiAgent() {

        try {
            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

            ObjectName mbeanName = new ObjectName("jmx:type=testLoader");

            TestLoader mbean = new TestLoader("test","com.class");

            mbs.registerMBean(mbean, mbeanName);

            int port=9000;

            //這個步驟很重要操骡,注冊一個端口九火,綁定url后用于客戶端通過rmi方式連接JMXConnectorServer
            LocateRegistry.createRegistry(port);
            //URL路徑的結(jié)尾可以隨意指定赚窃,但如果需要用Jconsole來進(jìn)行連接,則必須使用jmxrmi
            String urlStr="service:jmx:rmi:///jndi/rmi://localhost:"+port+"/jmxrmi";
            JMXServiceURL url = new JMXServiceURL(urlStr);
            JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
            jcs.start();
            System.out.println("rmi start: "+urlStr);
        } catch (MalformedObjectNameException e) {
            e.printStackTrace();
        } catch (InstanceAlreadyExistsException e) {
            e.printStackTrace();
        } catch (MBeanRegistrationException e) {
            e.printStackTrace();
        } catch (NotCompliantMBeanException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        RmiAgent rmiAgent=new RmiAgent();
    }
}

當(dāng)然岔激,我們實際使用的是tomcat等容器服務(wù)勒极,所以說下遠(yuǎn)程tomcat開啟jmx監(jiān)控服務(wù)。其實虑鼎,很簡單辱匿,只需要,我們在自定義等setnev.sh中定義

#開啟服務(wù)
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote
#指定ip
 -Djava.rmi.server.hostname=172.18.162.10  
#指定端口
-Dcom.sun.management.jmxremote.port=9999 
#指定是否需要密碼炫彩,如果為true匾七,需要指定用戶密碼
-Dcom.sun.management.jmxremote.authenticate=false 
#是否需要ssl
 -Dcom.sun.management.jmxremote.ssl=false"

此時再啟動tomcat,就是自動啟動tomcat等jmx了江兢。

對于客戶端來說乐尊,我們有很多,常用的如JVisualVM划址、JConsole等。下面說下兩種的使用方法限府。mac環(huán)境下夺颤,裝好jdk后,這些都是已經(jīng)集成好的(windows在java的bin目錄下可以找到相應(yīng)exe程序)胁勺,打開終端輸入JVisualVM就會自動啟動JVisualVM客戶端(對應(yīng)JConsole一樣的操作流程)


mac操作

JVisualVM功能是JConsole的升級版世澜,包含了很多功能,可以查看內(nèi)存署穗、cpu寥裂、線程、生成堆快照.....等等案疲。在JVisualVM使用JConsole 封恰,安裝JConsole插件即可,如下我安裝了JConsole褐啡、MBean诺舔、Btrace等插件:


安裝插件

插件安裝后,我們可以就可以在JVisualVM中使用JConsole备畦、MBean低飒、Btrace了。

  1. 看下對遠(yuǎn)程tomcat對監(jiān)控
    上面懂盐,我們已經(jīng)在tomcat的bin目錄下的setenv.sh文件中加入了啟動jmx的腳本褥赊,我們已經(jīng)可以通過客戶端鏈接了,鏈接地址看下配置文件:
#指定ip
 -Djava.rmi.server.hostname=172.18.162.10  
#指定端口
-Dcom.sun.management.jmxremote.port=9999

端口號是9999莉恼,我們用JVisualVM鏈接


鏈接遠(yuǎn)程tomcat

這樣我們就可以通過jmx來管理遠(yuǎn)程tomcat服務(wù)了拌喉,看下tomcat自帶的一些jmx使用


image.png

類似這種管理類我們看到了很多速那,可以通過jmx添加用戶,查看部署的app狀態(tài)等等管理模塊司光。

  1. 本地java服務(wù)監(jiān)控
    在我們本地調(diào)試java代碼時也可以使用琅坡,并且,可以使用Btrace進(jìn)行代碼注入残家。
    如上邊我本地運(yùn)行了一個LocalAgent的java程序


    本地監(jiān)控

對本地的java進(jìn)程就可以做相應(yīng)的監(jiān)控操作了榆俺,并且我們安裝了Btrace插件,可以通過Btrace腳本進(jìn)行代碼注入坞淮,如TestLoader類中有個loaderClasses方法茴晋,在不關(guān)服務(wù)的情況下,我想測一下此方法的執(zhí)行時間回窘。我們寫個Btrace腳本

/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
    /* put your code here */
   @OnMethod(
     clazz="jmx.beans.TestLoader", 
     method="loaderClasses"
   )   
   public static void onWebserviceEntry(@ProbeClassName String pcn, @ProbeMethodName String pmn) {
       println(Strings.strcat(Strings.strcat(pcn, "."), pmn));
   }

   @OnMethod(
     clazz="jmx.beans.TestLoader", 
      method="loaderClasses",
     location=@Location(Kind.RETURN)
   )   
   public static void onWebserviceReturn(@ProbeClassName String pcn , @ProbeMethodName String pmn, @Duration long d) {
       print("leaving web service ");
       println(Strings.strcat(Strings.strcat(pcn, "."), pmn));
       println(Strings.strcat("Time taken (msec) ", Strings.str(d / 1000)));
       println("==========================");
   }
}

腳本中通過onWebserviceReturn方法的@Duration參數(shù)打印了方法的執(zhí)行時間诺擅,這樣就在我們無需關(guān)閉服務(wù)器的情況下,添加了代理代碼啡直。但要注意Btrace執(zhí)行后烁涌,java類的class并不會還原,會一直存在酒觅,所以如果是生產(chǎn)環(huán)境要謹(jǐn)慎撮执。特別@OnMethod通配符的寫法,要修改所有匹配到的類舷丹。如果想了解Btrace更多內(nèi)容抒钱,更多腳本,可以參考官方文檔https://github.com/btraceio/btrace颜凯,里邊有大部分場景的樣例腳本:

image.png

好了谋币,上邊說了這么多,都是通過jmx延伸出症概,我們對于應(yīng)用監(jiān)控的一些方法蕾额。當(dāng)然還有很多,如對于遠(yuǎn)程debug的jpda穴豫、對于內(nèi)存分析的mat等等凡简,有時間可以一起討論下。jmx給我提供的是一種監(jiān)控思想精肃,如我們要實現(xiàn)自己的連接池秤涩、管理器等服務(wù),可以借鑒相應(yīng)的監(jiān)控管理方法司抱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筐眷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子习柠,更是在濱河造成了極大的恐慌匀谣,老刑警劉巖照棋,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異武翎,居然都是意外死亡烈炭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門宝恶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來符隙,“玉大人,你說我怎么就攤上這事垫毙∨撸” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵综芥,是天一觀的道長丽蝎。 經(jīng)常有香客問我,道長膀藐,這世上最難降的妖魔是什么屠阻? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮额各,結(jié)果婚禮上栏笆,老公的妹妹穿的比我還像新娘。我一直安慰自己臊泰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布蚜枢。 她就那樣靜靜地躺著缸逃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪厂抽。 梳的紋絲不亂的頭發(fā)上需频,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機(jī)與錄音筷凤,去河邊找鬼昭殉。 笑死,一個胖子當(dāng)著我的面吹牛藐守,可吹牛的內(nèi)容都是我干的挪丢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卢厂,長吁一口氣:“原來是場噩夢啊……” “哼乾蓬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慎恒,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤任内,失蹤者是張志新(化名)和其女友劉穎撵渡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體死嗦,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趋距,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了越除。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片节腐。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖廊敌,靈堂內(nèi)的尸體忽然破棺而出铜跑,到底是詐尸還是另有隱情,我是刑警寧澤骡澈,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布锅纺,位于F島的核電站,受9級特大地震影響肋殴,放射性物質(zhì)發(fā)生泄漏囤锉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一护锤、第九天 我趴在偏房一處隱蔽的房頂上張望官地。 院中可真熱鬧,春花似錦烙懦、人聲如沸驱入。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亏较。三九已至,卻和暖如春掩缓,著一層夾襖步出監(jiān)牢的瞬間雪情,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工你辣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巡通,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓舍哄,卻偏偏與公主長得像宴凉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子表悬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容