cassandra高級操作之JMX操作

来源:http://www.cnblogs.com/youzhibing/archive/2017/04/06/6674995.html
-Advertisement-
Play Games

需求場景 項目中有這麼個需求:統計集群中各個節點的數據量存儲大小,不是記錄數。 一開始有點無頭緒,後面查看cassandra官方文檔看到Monitoring章節,裡面說到:Cassandra中的指標使用Dropwizard Metrics庫進行管理。 這些指標可以通過JMX查詢,也可以使用多個內置和 ...


需求場景

  項目中有這麼個需求:統計集群中各個節點的數據量存儲大小,不是記錄數。

  一開始有點無頭緒,後面查看cassandra官方文檔看到Monitoring章節,裡面說到:Cassandra中的指標使用Dropwizard Metrics庫進行管理。 這些指標可以通過JMX查詢,也可以使用多個內置和第三方報告插件推送到外部監控系統(Jconsole)。那麼數據量存儲大小是不是也是cassandra的某項指標了? 帶著疑問,我通過Jconsole看到了cassandra的一些指標(先啟動cassandra, 運行  -> Jconsole),如下圖

  數據量存儲大小就在叫org.apache.cassandra.db的MBean中,具體會在之後介紹。

JMX定義

  引用JMX超詳細解讀中一段話:

JMX(Java Management Extensions)是一個為應用程式植入管理功能的框架。JMX是一套標準的代理和服務,實際上,用戶可以在任何Java應用程式中使用這些代理和服務實現管理。這是官方文檔上的定義,我看過很多次也無法很好的理解。
我個人的理解是JMX讓程式有被管理的功能,例如你開發一個WEB網站,它是在24小時不間斷運行,那麼你肯定會對網站進行監控,如每天的UV、PV是多少;又或者在業務高峰的期間,你想對介面進行限流,就必須去修改介面併發的配置值。   應用場景:中間件軟體WebLogic的管理頁面就是基於JMX開發的,而JBoss則整個系統都基於JMX構架,另外包括cassandra中各項指標的管理。   對於一些參數的修改,網上有一段描述還是比較形象的:   1、程式初哥一般是寫死在程式中,到要改變的時候就去修改代碼,然後重新編譯發佈。   2、程式熟手則配置在文件中(JAVA一般都是properties文件),到要改變的時候只要修改配置文件,但還是必須重啟系統,以便讀取配置文件里最新的值。   3、程式好手則會寫一段代碼,把配置值緩存起來,系統在獲取的時候,先看看配置文件有沒有改動,如有改動則重新從配置里讀取,否則從緩存里讀取。   4、程式高手則懂得物為我所用,用JMX把需要配置的屬性集中在一個類中,然後寫一個MBean,再進行相關配置。另外JMX還提供了一個工具頁,以方便我們對參數值進行修改。

  給我的感覺,jmx server進行監聽,jmx client進行請求的發送,以此達到通信的目的;cassandra的jmx server已經被cassandra實現,我們只需要實現jmx client,就能從cassandra進程中拿到我們需要的指標數據。

JMX Server

  1、 首先定義一個MBean介面

    介面的命名規範為以具體的實現類為首碼(這個規範很重要),動態代理的過程中需要用到這點。

public interface HelloMBean
{
    String getName();
    void setName(String name);
    
    void print();
}

  2、定義一個實現類

    實現上面的介面:

public class Hello implements HelloMBean
{
    
    private String name;
    
    @Override
    public String getName()
    {
        return this.name;
    }
    
    @Override
    public void setName(String name)
    {
        this.name = name;
    }
    
    @Override
    public void print()
    {
        System.out.println("hello, print");
    }
    
}

  3、定義一個jmx server,並啟動它

public class HelloService
{
    private static final int RMI_PORT = 8099;  
    private static final String JMX_SERVER_NAME = "TestJMXServer";
    private static final String USER_NAME = "hello";
    private static final String PASS_WORD = "world";
    
    public static void main(String[] args) throws Exception
    {
        HelloService service = new HelloService(); 
        service.startJmxServer();
    }
    
    private void startJmxServer() throws Exception
    {
        //MBeanServer mbs = MBeanServerFactory.createMBeanServer(jmxServerName);  
        MBeanServer mbs = this.getMBeanServer(); 
      
        // 在本地主機上創建並輸出一個註冊實例,來接收特定埠的請求
        LocateRegistry.createRegistry(RMI_PORT, null, RMISocketFactory.getDefaultSocketFactory()); 
        
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + RMI_PORT + "/" + JMX_SERVER_NAME);  
        System.out.println("JMXServiceURL: " + url.toString());  
        
        Map<String, Object> env = this.putAuthenticator();
        
        //JMXConnectorServer jmxConnServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);   // 不加認證 
        JMXConnectorServer jmxConnServer = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);      // 加認證 
        jmxConnServer.start(); 
    }
    
    private MBeanServer getMBeanServer() throws Exception
    {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        ObjectName objName = new ObjectName(JMX_SERVER_NAME + ":name=" + "hello");  
        mbs.registerMBean(new Hello(), objName);
        return mbs;
    }
    
    private Map<String, Object> putAuthenticator()
    {
        Map<String,Object> env = new HashMap<String,Object>();
        JMXAuthenticator auth = createJMXAuthenticator();
        env.put(JMXConnectorServer.AUTHENTICATOR, auth);

        env.put("com.sun.jndi.rmi.factory.socket", RMISocketFactory.getDefaultSocketFactory());
        return env;
    }
    
    private JMXAuthenticator createJMXAuthenticator() 
    {
        return new JMXAuthenticator() 
        {
            public Subject authenticate(Object credentials) 
            {
                String[] sCredentials = (String[]) credentials;
                if (null == sCredentials || sCredentials.length != 2)
                {
                    throw new SecurityException("Authentication failed!");
                }
                String userName = sCredentials[0];
                String password = sCredentials[1];
                if (USER_NAME.equals(userName) && PASS_WORD.equals(password)) 
                {
                    Set<JMXPrincipal> principals = new HashSet<JMXPrincipal>();
                    principals.add(new JMXPrincipal(userName));
                    return new Subject(true, principals, Collections.EMPTY_SET,
                            Collections.EMPTY_SET);
                }
                throw new SecurityException("Authentication failed!");
            }
        };
    }
}
View Code

  

  點下print按鈕,你會發現控制台會列印:hello, print。

  cassandra的jmx server已經自己實現了,我們不需要實現它,我們需要實現的是調用它。

JMX client

  這個是我們需要關註和實現的

  1、client端介面定義

    介面中定義的方法是我們需要調用的,方法名必須與server端暴露的方法一樣,通過server端動態生成client端的實例,實例中的方法只包括client端介面中定義的方法(若server端暴露的是屬性,那麼直接在屬性前加get,後面cassandra部分會講到)

public interface HelloClientMBean
{
    void print();          // 方法定義與server端暴露的方法一致
}

  2、連接jmx server

public class HelloClient implements AutoCloseable
{
    private static final String fmtUrl = "service:jmx:rmi:///jndi/rmi://[%s]:%d/TestJMXServer";
    private static final String ssObjName = "TestJMXServer:name=hello";
    private static final int defaultPort = 1099;                       // cassandra預設埠是7199
    final String host;
    final int port;
    private String username;
    private String password;
    
    private JMXConnector jmxc;
    private MBeanServerConnection mbeanServerConn;
    private HelloMBean hmProxy;
    
    /**
     * Creates a connection using the specified JMX host, port, username, and password.
     *
     * @param host hostname or IP address of the JMX agent
     * @param port TCP port of the remote JMX agent
     * @throws IOException on connection failures
     */
    public HelloClient(String host, int port, String username, String password) throws IOException
    {
        assert username != null && !username.isEmpty() && password != null && !password.isEmpty()
               : "neither username nor password can be blank";

        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
        connect();
    }
    
    /**
     * Creates a connection using the specified JMX host and port.
     *
     * @param host hostname or IP address of the JMX agent
     * @param port TCP port of the remote JMX agent
     * @throws IOException on connection failures
     */
    public HelloClient(String host, int port) throws IOException
    {
        this.host = host;
        this.port = port;
        connect();
    }
    
    /**
     * Creates a connection using the specified JMX host and default port.
     *
     * @param host hostname or IP address of the JMX agent
     * @throws IOException on connection failures
     */
    public HelloClient(String host) throws IOException
    {
        this.host = host;
        this.port = defaultPort;
        connect();
    }
    
    /**
     * Create a connection to the JMX agent and setup the M[X]Bean proxies.
     *
     * @throws IOException on connection failures
     */
    private void connect() throws IOException
    {
        JMXServiceURL jmxUrl = new JMXServiceURL(String.format(fmtUrl, host, port));
        Map<String,Object> env = new HashMap<String,Object>();
        if (username != null)
        {
            String[] creds = { username, password };
            env.put(JMXConnector.CREDENTIALS, creds);
        }

        env.put("com.sun.jndi.rmi.factory.socket", getRMIClientSocketFactory());

        jmxc = JMXConnectorFactory.connect(jmxUrl, env);
        mbeanServerConn = jmxc.getMBeanServerConnection();

        try
        {
            ObjectName name = new ObjectName(ssObjName);
            hmProxy = JMX.newMBeanProxy(mbeanServerConn, name, HelloMBean.class);
        }
        catch (MalformedObjectNameException e)
        {
            throw new RuntimeException(
                    "Invalid ObjectName? Please report this as a bug.", e);
        }
    }
    
    private RMIClientSocketFactory getRMIClientSocketFactory() throws IOException
    {
        if (Boolean.parseBoolean(System.getProperty("ssl.enable")))
            return new SslRMIClientSocketFactory();
        else
            return RMISocketFactory.getDefaultSocketFactory();
    }
    
    public void print()
    {
        hmProxy.print();
    }

    @Override
    public void close() throws Exception 
    {
        jmxc.close();
    }
}
View Code

  3、介面調用

public class JmxClient
{
    public static void main(String[] args) throws Exception
    {
        HelloClient client = new HelloClient("localhost", 8099, "hello", "world");
        client.print();
        client.close();
    }
    
}

    會在控制台列印:hello, print。

統計cassandra集群中各個節點的數據量存儲大小

  也分3步:

  1、client端介面定義

    因為我們只關心數據量存儲大小,所以我們只需要在介面定義一個方法

 

public interface StorageServiceMBean
{
    /** Human-readable load value.  Keys are IP addresses. */
    public Map<String, String> getLoadMap();            // cassandra端暴露的是屬性LoadMap,那麼此方法名由get加LoadMap組成, 那麼getLoad方法就可以獲取LoadMap的值
}

  2、連接jmx server    

    cassandra-env.sh配置文件中有cassandra的JMX預設埠:JMX_PORT="7199"

public class CassNodeProbe implements AutoCloseable
{
    private static final String fmtUrl = "service:jmx:rmi:///jndi/rmi://[%s]:%d/jmxrmi";
    private static final String ssObjName = "org.apache.cassandra.db:type=StorageService";
    private static final int defaultPort = 7199;
    final String host;
    final int port;
    private String username;
    private String password;
    
    private JMXConnector jmxc;
    private MBeanServerConnection mbeanServerConn;
    private StorageServiceMBean ssProxy;
    
    /**
     * Creates a NodeProbe using the specified JMX host, port, username, and password.
     *
     * @param host hostname or IP address of the JMX agent
     * @param port TCP port of the remote JMX agent
     * @throws IOException on connection failures
     */
    public CassNodeProbe(String host, int port, String username, String password) throws IOException
    {
        assert username != null && !username.isEmpty() && password != null && !password.isEmpty()
               : "neither username nor password can be blank";

        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
        connect();
    }
    
    /**
     * Creates a NodeProbe using the specified JMX host and port.
     *
     * @param host hostname or IP address of the JMX agent
     * @param port TCP port of the remote JMX agent
     * @throws IOException on connection failures
     */
    public CassNodeProbe(String host, int port) throws IOException
    {
        this.host = host;
        this.port = port;
        connect();
    }
    
    /**
     * Creates a NodeProbe using the specified JMX host and default port.
     *
     * @param host hostname or IP address of the JMX agent
     * @throws IOException on connection failures
     */
    public CassNodeProbe(String host) throws IOException
    {
        this.host = host;
        this.port = defaultPort;
        connect();
    }
    
    /**
     * Create a connection to the JMX agent and setup the M[X]Bean proxies.
     *
     * @throws IOException on connection failures
     */
    private void connect() throws IOException
    {
        JMXServiceURL jmxUrl = new JMXServiceURL(String.format(fmtUrl, host, port));
        Map<String,Object> env = new HashMap<String,Object>();
        if (username != null)
        {
            String[] creds = { username, password };
            env.put(JMXConnector.CREDENTIALS, creds);
        }

        env.put("com.sun.jndi.rmi.factory.socket", getRMIClientSocketFactory());

        jmxc = JMXConnectorFactory.connect(jmxUrl, env);
        mbeanServerConn = jmxc.getMBeanServerConnection();

        try
        {
            ObjectName name = new ObjectName(ssObjName);
            ssProxy = JMX.newMBeanProxy(mbeanServerConn, name, StorageServiceMBean.class);
        }
        catch (MalformedObjectNameException e)
        {
            throw new RuntimeException(
                    "Invalid ObjectName? Please report this as a bug.", e);
        }
    }
    
    private RMIClientSocketFactory getRMIClientSocketFactory() throws IOException
    {
        if (Boolean.parseBoolean(System.getProperty("ssl.enable")))
            return new SslRMIClientSocketFactory();
        else
            return RMISocketFactory.getDefaultSocketFactory();
    }
    
    public Map<String, String> getCassClusterStorage()
    {
        return ssProxy.getLoadMap();
    }

    @Override
    public void close() throws Exception 
    {
        jmxc.close();
    }
}
View Code

  3、介面調用

public class JMXTest
{
    
    public static void main(String[] args) throws Exception
    {
        CassNodeProbe prode = new CassNodeProbe("127.0.0.1");
        Map<String, String> nodeStorages = prode.getCassClusterStorage();
        System.out.println(nodeStorages);
        prode.close();
    }
    
}

  最後得到結果:{127.0.0.1=266.36 KB}

  cassandra的jmx 認證訪問我就不做演示了,大家自己去實現。

cassandra jmx client實現:cassandra-all

  cassandra給我們提供了工具jar,jmx server暴露的在這個工具jar中都有對應的請求方式;

  如若大家用到的很少則可以自己實現,而不需要用cassandra-all,當然我們可以拷貝cassandra-all中我們需要的代碼到我們的工程中,那麼我們就可以不用引用此jar,但是又滿足了我們的需求

<dependency>
    <groupId>org.apache.cassandra</groupId>
    <artifactId>cassandra-all</artifactId>
    <version>2.1.14</version>
</dependency>

  工程附件

參考:

  http://www.cnblogs.com/FlyAway2013/p/jmx.html

  http://www.cnblogs.com/dongguacai/p/5900507.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 此篇為針對Objective-c語言入門的基礎知識,為了能讓大家更清楚的理解,此整理中編寫了許多的代碼案例和部分截圖,如有錯誤之處,望指正,願與您相互交流學習,共同進步! "會飛的猴子_阿新" (同時還要向刀哥致敬) 本篇目標是: 理解記憶體五大區域及各自的職責 目錄結構 00.簡述 01. 分配和釋 ...
  • 在和sever後臺交互的過程中、有時候、他們需要我們iOS開發者以“application/json”形式上傳。 ...
  • Paint 畫筆 ,即用來繪製圖形的"筆" 前面我們知道了Paint的一些基本用法: 不過我們會發現,這樣畫出的線條都是筆筆直直的,能滿足需求,但是美觀上並不好看。 這就需要使用到Paint類 更多的一些方法了 首先,看下最簡單設置的線條 一、線條路徑樣式 設置路徑樣式;取值類型是所有派生自Path ...
  • 安裝 Android Development Environment http://www.cnblogs.com/youchihwang/p/6645880.html 2. answer : no 3. Installing Repo 4. ARM Compiler Tools 5.01 upda ...
  • 由OpenDigg 出品的安卓開源項目周報第十五期來啦。我們的安卓開源周報集合了OpenDigg一周來新收錄的優質的安卓開源項目,方便安卓開發人員便捷的找到自己需要的項目工具。 ...
  • 圖片載入涉及到圖片的緩存、圖片的處理、圖片的顯示等。四種常用的圖片載入框架,分別是Fresco、ImageLoader、 Picasso、 Glide... ...
  • 為了ios開發中tableview讀取數據的方便,先弄了一個只有字母的json表,再將未處理的數據添加到上面。僅用於將首字母作為section head。 [{"initial":"A","list":[]},{"initial":"B","list":[]},{"initial":"C","lis ...
  • 安裝rz工具和wget工具:分別如下: sudo yum install lrzsz;yum install wget 安裝cmake編譯工具:rz -y 選擇下載的cmake工具上傳到伺服器中 安裝c、c++:yum -y install gcc-c++ 環境參數:Linux:Centos6.5, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...