Appendix A. JMX 모니터링 예제

내용 목차

A.1. Servlet Thread Info 모니터링 예제
A.2. Thread Pool Info 모니터링 예제
A.3. JVM Info 모니터링 예제
A.4. JDBC DataSource(DB Connection Pool) 모니터링 예제

JEUS MBean 정보를 모니터링하는 4가지 중요한 예제를 설명한다.

설명하는 모든 예제에서 MBean 서버 정보를 조회할 때 RemoteMBeanServerFactory를 이용한다. MBean 서버를 얻어오는 다른 방법에 대한 자세한 내용은 “제3장 JMX 애플리케이션 개발”을 참고한다.

A.1. Servlet Thread Info 모니터링 예제

Servlet Thread Info 모니터링 예제는 웹 컨테이너의 리스너의 정보, 특히 Thread Pool과 각 Thread에 대한 정보를 모니터링한다.

이 예제를 통해 모니터링하는 정보는 다음과 같다.

  • 현재 할당된 Worker Thread의 수(current thread count)

  • Thread Pool을 유지하는 최대 Thread의 개수(max thread count)

  • Wait-Queue에 적체된 클라이언트의 수(wait queue count)

다음은 Servlet Thread Info 모니터링 예제이다.

package monitoring;

import java.util.*;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.ObjectInstance;
import javax.management.MalformedObjectNameException;
import javax.management.MBeanServerInvocationHandler;
import jeus.management.j2ee.servlet.*;

public class ServletThreadInfo {
    public void showInfo(MBeanServerConnection mBeanServer, String targetName)
    throws Exception {
        System.out.println("=== Servlet Thread Info ===");

        // Get the object name of the servlet engine using JMX Standard API 
        // Otherwise, the object name could be queried through MBeanServer.
        // Please see JEUS MBean API javadoc for more concrete name, key properties.
        ObjectName name = new ObjectName("JEUS:jeusType=WebEngine,J2EEServer=" + 
                          targetName + ",*");
        Set names = mBeanServer.queryMBeans(name, null);
        if (names == null || names.size() == 0) {
            System.out.println("there is no servlet engine");
            return;
        }

        // Choose one servlet engine from the returned object names
        Iterator it = names.iterator();
        ObjectName fullName = null;
        while (it.hasNext()) {
            fullName = ((ObjectInstance)it.next()).getObjectName();
            break;
        }

        assert fullName != null;

        WebEngineMoMBean engineMBean = 
       (WebEngineMoMBean)MBeanServerInvocationHandler.newProxyInstance(mBeanServer,
       fullName, WebEngineMoMBean.class, false);

        // Get listeners from WebEngine
        String[] listeners = engineMBean.getWebListeners();
        if(listeners == null || listeners.length == 0){
        	return;
        }
        
        String webEngineName = engineMBean.getObjectName().getKeyProperty("name");
        for(int i = 0; i < listeners.length; i++) {
           ObjectName listener = new ObjectName(listeners[i]);
        
           WebListenerMoMBean listenerMBean= 
            (WebListenerMoMBean) MBeanServerInvocationHandler.newProxyInstance
            (mBeanServer, listener, WebListenerMoMBean.class, false);

            String[] tpoolNames = listenerMBean.getThreadPools();

            // Get stats from thread pools
            if (tpoolNames != null) {
               showThreadPoolStats(mBeanServer, webEngineName, tpoolNames);
            }    
        }
        System.out.println();
    }

    private void showThreadPoolStats(MBeanServerConnection mBeanServer, 
                                     String contextGroupName, 
                                     String[] tpoolNames) 
    throws MalformedObjectNameException {
        for (int k = 0; k < tpoolNames.length; k++) {
          ObjectName tpool = new ObjectName(tpoolNames[k]);
          System.out.println("[MBean] " + tpool);
          ThreadPoolMoMBean tpoolMBean = 
            (ThreadPoolMoMBean)MBeanServerInvocationHandler.newProxyInstance(
             mBeanServer,tpool, ThreadPoolMoMBean.class, false);
          ThreadPoolStatsImpl stats = (ThreadPoolStatsImpl) tpoolMBean.getstats();
          System.out.println("Listener: " + contextGroupName 
                + "/" + tpool.getKeyProperty("name"));
         System.out.println("- current thread count      : " 
                + stats.getAllThreadCount().getCount());
         System.out.println("- max thread count          : " 
                + stats.getMaxThreadCount().getCount());

         if (stats.getStatisticVersion() == ThreadPoolStatsImpl.NIO_VERSION) {
                // pipeline
                System.out.println("- total connection count    : " 
                    + stats.getTotalConnectionCount().getCount());
                System.out.println("- max queue count           : " 
                    + stats.getMaxQueueCount().getCount());
                System.out.println("- current queue count       : " 
                    + stats.getCurrentQueueCount().getCount());
                System.out.println("- remain queue count        : " 
                    + stats.getRemainQueueCount().getCount());
                System.out.println("- peak queue count          : " 
                    + stats.getPeakQueueCount().getCount());
                System.out.println("- total queue count         : " 
                    + stats.getTotalQueueCount().getCount());
                System.out.println("- difference queue 1m count  : " 
                    + stats.getDifferenceQueue1MCount().getCount());
                System.out.println("- difference queue 5m count  : " 
                    + stats.getDifferenceQueue5MCount().getCount());
                System.out.println("- difference queue 15m count : " 
                    + stats.getDifferenceQueue15MCount().getCount());
                System.out.println("- overflow queue count      : " 
                    + stats.getOverflowCount().getCount());
                System.out.println("- average queue time        : " 
                    + stats.getQueueWaitTimeAverage().getCount() + "(ms)");
            } else {
                System.out.println("- wait queue count          : " 
                    + stats.getWaitQueueCount().getCount());
            }

            System.out.println();
        }
    }
}

A.2. Thread Pool Info 모니터링 예제

Thread Pool Info 모니터링 예제는 JEUS 에서 사용하는 여러 Thread Pool의 상태를 모니터링한다.

이 예제를 통해 모니터링하는 정보는 다음과 같다.

  • Thread Pool 크기(size)에 대한 정보

  • Thread Pool 통계(stats) 정보

  • Thread 들의 수행시간에 대한 통계(execution time stats) 정보

  • Waiting-Queue 크기에 대한 통계(size stats) 정보

다음은 Thread Info 모니터링 예제이다.

package monitoring;

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;

import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.j2ee.statistics.TimeStatistic;
import javax.management.j2ee.statistics.RangeStatistic;
import javax.naming.*;

import jeus.jndi.JNSConstants;
import jeus.management.JMXConstants;
import jeus.management.RemoteMBeanServerFactory;
import jeus.management.j2ee.thread.ThreadPoolMBean;
import jeus.management.j2ee.thread.stats.ThreadPoolStats;

public class ThreadPoolInfo {
    public void showInfo(MBeanServerConnection mBeanServer, String name)
    throws Exception {
        System.out.println("=== ThreadPool Info ===");

        // Get the object names of the thread pools. 
        // Please see JEUS MBean API javadoc for more concrete name, key 
        // properties.
        ObjectName objectNames = new ObjectName("JEUS:jeusType=ThreadPool,*");
        Set tpMBeans = mBeanServer.queryMBeans(objectNames, null);
        for (Iterator i = tpMBeans.iterator(); i.hasNext();) {
          ObjectName mbeanName = ((ObjectInstance) i.next()).getObjectName();

          // thread pool name
          System.out.println("[Thread-pool : " 
                + mbeanName.getKeyProperty("name") + "]");

          System.out.println("[MBean] " + mbeanName);

          ThreadPoolMBean pool = 
             (ThreadPoolMBean)MBeanServerInvocationHandler.newProxyInstance(
                   mBeanServer, mbeanName, ThreadPoolMBean.class, false);

          // ThreadPool Size
          System.out.println("-size : " + pool.getPoolSize());
          System.out.println("-core size : " + pool.getCorePoolSize());
          System.out.println("-largest Size : " + pool.getLargestPoolSize());
          System.out.println("-max size : " + pool.getMaximumPoolSize());
          System.out.println("-queue size : " + pool.getWorkQueueSize());

          // ThreadPool Stats
          ThreadPoolStats stats = (ThreadPoolStats) pool.getstats();
          TimeStatistic executionTimeStats = stats.getThreadExecutionTime();
          TimeStatistic waitingTimeStats = stats.getQueueWaitingTime();

          System.out.println("# Thread Execution Time Stats");
          System.out.println("--unit : " + executionTimeStats.getUnit());
          System.out.println("--count : " + executionTimeStats.getCount());
          System.out.println("--min time : " + executionTimeStats.getMinTime());
          System.out.println("--max time : " + executionTimeStats.getMaxTime());

          System.out.println("# Queue Waiting Time Stats");
          System.out.println("--unit : " + waitingTimeStats.getUnit());
          System.out.println("--count : " + waitingTimeStats.getCount());
          System.out.println("--min time : " + waitingTimeStats.getMinTime());
          System.out.println("--max time : " + waitingTimeStats.getMaxTime());
      }
   }
}

A.3. JVM Info 모니터링 예제

JVM Info 모니터링 예제는 JEUS의 노드 또는 컨테이너의 JVM에 대해 모니터링을 한다. 이 예제를 통해 모니터하는 정보는 다음과 같다.

  • JVM의 Total size 정보

  • JVM의 Heap size 정보

  • JVM의 Uptime 정보

다음은 JVM Info 모니터링 예제이다.

package monitoring;

import java.util.Iterator;
import java.util.Set;

import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.j2ee.statistics.BoundedRangeStatistic;
import javax.management.j2ee.statistics.CountStatistic;
import javax.management.j2ee.statistics.RangeStatistic;

import jeus.management.j2ee.JVMMBean;
import jeus.management.j2ee.statistics.JVMStatsImpl;

public class JVMInfo {    
    public void showInfo(MBeanServerConnection mBeanServer, String targetName)
    throws Exception {
        System.out.println("=== JVM Statistics ===");

        ObjectName objectName = new ObjectName("JEUS:j2eeType=JVM,name=" 
                                + targetName + ",*");
        Set jvmMBeans = mBeanServer.queryMBeans(objectName, null);
        for (Iterator i = jvmMBeans.iterator(); i.hasNext();) {
            ObjectName objName = ((ObjectInstance) i.next()).getObjectName();
            System.out.println("[MBean] " + objName);
            
            // JVMMBean Stats
            JVMMBean jvm = (JVMMBean)MBeanServerInvocationHandler.newProxyInstance(
                           mBeanServer, objName, JVMMBean.class, false);

            JVMStatsImpl jvmstatsimpl = (JVMStatsImpl) jvm.getstats();
            RangeStatistic totalSize = jvmstatsimpl.getTotalSize();
            BoundedRangeStatistic heapSize = jvmstatsimpl.getHeapSize();
            CountStatistic upTime = jvmstatsimpl.getUpTime();

            // JVM Total Size
            System.out.println("[Total Size]");
            System.out.println("-unit : " + totalSize.getUnit());
            System.out.println("-current : " + totalSize.getCurrent());
            System.out.println("-min size : " + totalSize.getLowWaterMark());
            System.out.println("-max size : " + totalSize.getHighWaterMark());

            // JVM Heap Size
            System.out.println("[Heap Size]");
            System.out.println("-unit : " + heapSize.getUnit());
            System.out.println("-current : " + heapSize.getCurrent());
            System.out.println("-min Size : " + heapSize.getLowWaterMark());
            System.out.println("-max Size : " + heapSize.getHighWaterMark());
            System.out.println("-lower bound : " + heapSize.getLowerBound());
            System.out.println("-upper bound : " + heapSize.getUpperBound());

            // JVM UpTime
            System.out.println("[Up Time]");
            System.out.println("-unit : " + upTime.getUnit());
            System.out.println("-count : " + upTime.getCount());
            System.out.println("-start time : " + upTime.getStartTime());
        }
    }
}

A.4. JDBC DataSource(DB Connection Pool) 모니터링 예제

본 절에서 설명하는 예제는 서버에 존재하는 DB Connection Pool을 모니터링한다. 여기에는 javax.management.remote.JMXConnector을 이용하여 MBean Connection을 얻는 예제도 함께 포함되어 있다.

이 예제의 내용은 다음과 같다.

  • 특정 서버의 MBean 서버로 연결한다.

  • jeus.management.j2ee.JDBCResourceMBean을 조회해서 현재 생성된 Connection Pool들의 정보를 주기적으로 파일에 남긴다.

    참고

    Connection Pool은 기동할 때 생성되지 않고 실제 서비스 호출 시점에 생성된다.

다음은 DB Connection Pool 모니터링 예제이다.

package monitoring;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;

import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.j2ee.statistics.JDBCConnectionPoolStats;
import javax.management.j2ee.statistics.JDBCStats;
import javax.management.remote.JMXConnector;
import javax.naming.Context;
import javax.naming.InitialContext;

import jeus.management.JMXConstants;
import jeus.management.j2ee.JDBCResourceMBean;

public class DBStatsClient {
    public static void main(String[] args) {
        String serverName = "changeplz"; //e.g. server1

        Hashtable<String, Object> env = new Hashtable<String,Object>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "jeus.jndi.JNSContextFactory");
        env.put(Context.URL_PKG_PREFIXES, "jeus.jndi.jns.url");
        env.put(Context.PROVIDER_URL, "localhost:9736");
        MBeanServerConnection mbsc;
        try {
            InitialContext ctx = new InitialContext(env);
            JMXConnector connector = (JMXConnector)ctx.lookup(
                    JMXConstants.JNDI_BINDING_PREFIX + serverName);
            connector.connect();
            mbsc = connector.getMBeanServerConnection();
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        System.out.println("mbean server connection successfully established");
        Set mbeans;
        try {
            ObjectName dbstats = new ObjectName("JEUS:j2eeType=JDBCResource,*");
            mbeans = mbsc.queryMBeans(dbstats, null);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        System.out.println("Successfully get JDBCResource");
        File file = new File(serverName+".log");
        FileOutputStream stream = null;
        try {
            stream = new FileOutputStream(file);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        while(true) {
          for (Iterator iter = mbeans.iterator();iter.hasNext();) {
             ObjectName jdbcResourceMBeanName = 
                    ((ObjectInstance) iter.next()).getObjectName();
             JDBCResourceMBean jdbcResource = 
                (JDBCResourceMBean) MBeanServerInvocationHandler.newProxyInstance(
                    mbsc, jdbcResourceMBeanName,
                    JDBCResourceMBean.class, false);
             JDBCStats jdbcStats = (JDBCStats) jdbcResource.getstats();
             SimpleDateFormat format = new SimpleDateFormat("[MM-dd]HH:mm:ss");
             StringBuilder builder = new StringBuilder();
             builder.append("[STA] ");
            for (JDBCConnectionPoolStats cpStats : jdbcStats.getConnectionPools())
            {
                String output =format.format(new Date(System.currentTimeMillis()))
                        + " name:[" +cpStats.getJdbcDataSource() + "]"
                        + " total:[" +cpStats.getPoolSize().getCurrent() +"]"
                        + " use:[" + (cpStats.getPoolSize().getCurrent() 
                                 - cpStats.getFreePoolSize().getCurrent()) +"]  ";
                builder.append(output);
             }
           builder.append("\n");
           try {
                    stream.write(builder.toString().getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
            }
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                return;
            }
        }
    }
}