19장. JBoss EAP 7 모니터링

Name

Date

Reason For Changes

Version

오픈나루

2013/11

Initial Version

1.0

시스템 운영에서 서버의 가동 상태나 자원의 사용 상황에 대한 모니터링은 시스템의 안정적인 운영에서 매우 중요한 요소이다. 자원의 사용 상황을 계속 모니터링하여 분석하면 서비스의 경향을 파악할 수 있다.

이번 장에서는 시스템의 운영 및 관리에서 중요한 서버의 모니터링 방법과 서브시스템 관련 자원, 배포된 애플리케이션에 대한 모니터링 방법에 설명한다. 또, Groovy스크립트나 Java코드에서 모니터링 데이터를 수집하기 위한 방법도 설명한다.

19-1.서버 모니터링

JBoss EAP 6의 서버기본 정보는 platform-mbean 코어 서비스(core-service=platform-mbean)에서 얻을 수 있다. 다음과 같은 자원 정보를 제공하고 있다.

  • OS정보

  • 메모리 정보

  • Thread 정보

  • 런타임 정보

OS 정보

실행 중인 JBoss EAP 6 서버의 OS 기본 정보를 확인할 수 있다. 주요 속성값은 아래와 같다.

속성 설명

name

OS 이름

arch

OS 아키텍쳐

version

OS 버전 정보

available-processors

사용 가능한 프로세서의 개수

system-load-average

시스템 로드 평균

표 1. OS의 기본 정보

CLI로 다음과 같이OS정보를 출력한다.

[standalone@localhost:9999 /] /core-service=platform-mbean/type=operating-system:read-resource

{

  "outcome" => "success",
  "result" => {
    "name" => "Linux",
    "arch" => "amd64",
    "version" => "2.6.32-279.el6.x86_64",
    "available-processors" => 4,
    "system-load-average" => 0.0
  }
}

자바 메모리의 정보

실행중인 JBoss EAP 6 서버의 힙 메모리 영역(heap-memory-usage)과 Non 힙 메모리 영역(non-heap-memory-usage)의 사용 현황을 확인할 수 있다. 메모리 정보에 대한 주요 속성값은 다음 표와 같다.

속성 설명

init

초기에 할당한 메모리 양

used

현재 사용하고 있는 메모리 양

committed

현재 사용 가능한 메모리 양

max

JVM이 할당하는 메모리의 최댓값

표 2. 메모리 정보의 주요 속성

아래와 같이 CLI로 메모리의 사용 현황을 출력한다.

[standalone@localhost:9999 /] /core-service=platform-mbean/type=memory:read-attribute(name=heap-memory-usage)

{
  "outcome" => "success",
  "result" => {
    "init" => 1366294528L,
    "used" => 106635768L,
    "committed" => 1309081600L,
    "max" => 1309081600L
  }
}

스레드 정보

실행 중인 JBoss EAP 6 서버의 스레드 정보를 얻을 수 있다. 스레드 정보의 주요 속성값은 다음 표와 같다.

속성 설명

all-thread-ids

실행중인 스레드 ID 목록

current-thread-user-time

현재 스레드의 사용자 시간. 단위는 nanoseconds

current-thread-cpu-time

현재 스레드의 CPU 시간

thread-count

현재 사용중인 스레드 개수

peak-thread-count

스레드 개수의 피크시 값

daemon-thread-count

데몬 스레드의 개수

total-started-thread-count

서버 구동 후 시작된 스레드 합계

표 3. 스레드 정보의 주요 속성

아래와 같이 CLI로 JBoss가 현재 사용하는 스레드 개수를 얻을 수 있다.

[standalone@localhost:9999 /] cd /core-service=platform-mbean/type= threading

[standalone@localhost:9999 type=threading] :read-attribute(name=thread-count)

{
  "outcome" => "success",
  "result" => 39
}

[standalone@localhost:9999 /] /core-service=platform-mbean/type=threading:read-resource

{
  "outcome" => "success",
  "result" => {
    "all-thread-ids" => [
      102L,
      101L,
      100L,
      99L,
      …생략 …
      3L,
      2L
    ],
    "thread-contention-monitoring-supported" => true,
    "thread-cpu-time-supported" => true,
    "current-thread-cpu-time-supported" => true,
    "object-monitor-usage-supported" => true,
    "synchronizer-usage-supported" => true,
    "thread-contention-monitoring-enabled" => false,
    "thread-cpu-time-enabled" => true,
    "thread-count" => 38,
    "peak-thread-count" => 81,
    "total-started-thread-count" => 95L,
    "daemon-thread-count" => 13,
    "current-thread-cpu-time" => 0L,
    "current-thread-user-time" => 0L
  }
}

런타임 상황

JBoss EAP 6의 기본 정보 및 가동 상태 정보를 수집할 수 있다. ‘/core-service=platform-mbean/type=runtime’에서 확인할 수 있는 것은 Java Runtime 정보와 클래스 패스, 시스템 프로퍼티 등 Java 실행환경에 대한 정보들이다.

옵션 설명 CLI 명령

name

호스트 이름

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=name)

{
  "outcome" => "success",
  "result" => "29268@opennaru"
}

vm-name

Java VM 이름

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=vm-name)

{
  "outcome" => "success",
  "result" => "Java HotSpot(TM) 64-Bit Server VM"
}

vm-vendor

Java VM 업체

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=vm-vendor)

{
  "outcome" => "success",
  "result" => "Sun Microsystems Inc."
}

vm-version

Java VM 버전

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=vm-version)

{
  "outcome" => "success",
  "result" => "20.45-b01"
}

spec-name

표준 이름

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=spec-name)

{
  "outcome" => "success",
  "result" => "Java Virtual Machine Specification"
}

spec-vendor

표준 벤더

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=spec-vendor)

{
  "outcome" => "success",
  "result" => "Sun Microsystems Inc."
}

spec-version

표준 버전

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=spec-version)

{
  "outcome" => "success",
  "result" => "1.0"
}

management-spec-version

Management 표준 버전

[standalone@localhost:9999 /] /core-service==platform-mbean/type=runtime:read-attribute(name=management-spec-version)

{
  "outcome" => "success",
  "result" => "1.2"
}

class-path

클래스 패스

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=class-path)

{
  "outcome" => "success",
  "result" => "/products/test/jbossEAP61/jboss-eap-6.1/jboss-modules.jar"
}

library-path

라이브러리 패스

[source, cli] ---- [standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=library-path)

{ "outcome" ⇒ "success", "result" ⇒ "/usr/java/jdk1.6.0_45/jre/lib/amd64/server:/usr/java/jdk1.6.0_45/jre/lib/amd64:/usr/java/jdk1.6.0_45/jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib" } ----

boot-class-path

부트 클래스 패스

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=boot-class-path)

{
  "outcome" => "success",
  "result" => "/usr/java/jdk1.6.0_45/jre/lib/resources.jar:/usr/java/jdk1.6.0_45/jre/lib/rt.jar:/usr/java/jdk1.6.0_45/jre/lib/sunrsasign.jar:/usr/java/jdk1.6.0_45/jre/lib/jsse.jar:/usr/java/jdk1.6.0_45/jre/lib/jce.jar:/usr/java/jdk1.6.0_45/jre/lib/charsets.jar:/usr/java/jdk1.6.0_45/jre/lib/modules/jdk.boot.jar:/usr/java/jdk1.6.0_45/jre/classes"
}

input-arguments

Java VM Arguments

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=input-arguments)

{
  "outcome" => "success",
  "result" => [
    "-D[Standalone]",
    "-XX:+UseCompressedOops",
    "-Xms1303m",
    "-Xmx1303m",
    "-XX:MaxPermSize=256m",
    "-Djava.net.preferIPv4Stack=true",
    "-Djboss.modules.system.pkgs=org.jboss.byteman",
    "-Djava.awt.headless=true",
    "-Dorg.jboss.boot.log.file=/products/test/jbossEAP61/jboss-eap-6.1/standalone/log/server.log",
    "-Dlogging.configuration=file:/products/test/jbossEAP61/jboss-eap-6.1/standalone/configuration/logging.properties"
  ]

}

system-properties

시스템 프로퍼티

[[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=system-properties)

{
  "outcome" => "success",
  "result" => {
    "[Standalone]" => "",
    "catalina.home" => "/products/test/jbossEAP61/jboss-eap-6.1/standalone/tmp",
    "file.encoding" => "UTF-8",
    "jboss.bind.address.management" => "0.0.0.0",
    "jboss.home.dir" => "/products/test/jbossEAP61/jboss-eap-6.1",
    … 생략 …
    "user.home" => "/products/test/jbossEAP 61",
    "user.language" => "ko",
    "user.name" => "jboss",
    "user.timezone" => "ROK"
  }
}

start-time

JBoss 실행 시간

[standalone@localhost:9999 /] /core-service=platform-mbean/type=runtime:read-attribute(name=start-time)

{
  "outcome" => "success",
  "result" => 1386029233021L
}

데이터소스

JBoss EAP 6 데이터소스 서브시스템의 연결 풀의 상황을 ‘/subsystem=datasources/data-source=<데이터소스명>/statistics=pool:read-resource(include-runtime=true, include-defaults=true)’ 에서 확인할 수 있다.

다음 예제는 CLI로 ExampleDS 의 데이터소스 접속 풀의 런타임 정보를 얻는 것이다.

[standalone@localhost:9999 /] /subsystem=datasources/data-source=ExampleDS/statistics=pool:read-resource(include-runtime=true,include-defaults=true)

{
  "outcome" => "success",
  "result" => {
    "ActiveCount" => "0",
    "AvailableCount" => "20",
    "AverageBlockingTime" => "0",
    "AverageCreationTime" => "0",
    "CreatedCount" => "0",
    "DestroyedCount" => "0",
    "InUseCount" => "0",
    "MaxCreationTime" => "0",
    "MaxUsedCount" => "0",
    "MaxWaitCount" => "0",
    "MaxWaitTime" => "0",
    "TimedOut" => "0",
    "TotalBlockingTime" => "0",
    "TotalCreationTime" => "0"
  }
}

데이터소스 정보의 주요 속성값은 다음 표와 같다.

속성 설명

ActiveCount

현재 사용 중인 연결 개수

AvailableCount

사용 가능한 연결 개수

AverageBlockingTime

연결을 위해서 대기했던 평균 시간(ms)

AverageCreationTime

데이터베이스와 연결에 걸린 평균 시간(ms)

CreatedCount

지금까지 만들어진 연결 개수

DestroyedCount

지금까지 소멸된 연결 개수

MaxCreationTime

데이터베이스와 연결에 걸린 최대 시간(ms)

MaxUsedCount

지금까지 동시에 사용된 최대 연결수

MaxWaitCount

연결을 얻기 위해 대기한 스레드의 최댓값

MaxWaitTime

연결을 얻기 위해 대기한 대기 시간의 최댓값

TimedOut

접속을 얻기 위해 기다리다 타임아웃된 스레드의 개수

TotalBlockingTime

연결을 위해 블록킹했던 시간의 총합

TotalCreationTime

연결을 생성하기 위해 걸린 시간의 총합

표 4. 데이터소스 정보의 주요 속성값

AJP 커넥터

웹 애플리케이션의 동작 상태를 모니터링하기 위해서는 웹 서브 시스템의 접속상황 정보를 확인하면 된다. 웹 서브시스템은 커넥터를 제공하는데 웹 서버를 통해 요청을 받고 있다면, AJP 커넥터의 정보를 확인하면 된다.

‘/subsystem=web/connector=ajp:read-resource(include-runtime=true)’에서 실행 중인 JBoss EAP 6 서버의 ajp 커넥터 정보를 얻을 수 있다.

http 커넥터를 사용하는 경우 ‘/subsystem=web/connector=http:read-resource(include-runtime=true)’에서 웹 서브시스템의 http 커넥터 정보를 얻을 수 있다.

ajp 커넥터의 주요 속성값은 다음 표와 같다.

속성 설명

bytesReceived

커넥터가 수신한 바이트 수

bytesSent

커넥터가 전송한 바이트 수

enable-lookups

서블릿 API에서 DNS를 조회하는지 정보

enabled

커넥터를 사용할 것인지 지정

errorCount

커넥터에서 요청 처리 시 발생한 에러의 개수

max-connections

최대 동시 접속 연결 수

max-post-size

컨테이너가 처리 가능한 POST의 최대 크기(bytes)

maxTime

요청 처리에 걸린 최대 시간

processingTime

커넥터의 처리 시간(ms)

requestCount

커넥터가 처리한 요청 개수

bytesReceived

커넥터가 받은 메시지의 총 바이트 수

bytesSent

커넥터가 전송한 메시지의 총 바이트 수

표 5. AJP 커넥터의 주요 속성값

다음과 같이 CLI로 AJP 커넥터의 런타임 정보를 얻을 수 있다.

[standalone@localhost:9999 /] /subsystem=web/connector=http:read-resource(include-runtime=true)

{
  "outcome" => "success",
  "result" => {
    "bytesReceived" => "0",
    "bytesSent" => "0",
    "configuration" => undefined,
    "enable-lookups" => false,
    "enabled" => true,
    "errorCount" => "0",
    "executor" => undefined,
    "max-connections" => undefined,
    "max-post-size" => 2097152,
    "max-save-post-size" => 4096,
    "maxTime" => "0",
    "name" => "http",
    "processingTime" => "0",
    "protocol" => "HTTP/1.1",
    "proxy-name" => undefined,
    "proxy-port" => undefined,
    "redirect-port" => 443,
    "requestCount" => "0",
    "scheme" => "http",
    "secure" => false,
    "socket-binding" => "http",
    "ssl" => undefined,
    "virtual-server" => undefined
  }
}

EJB

EJB3 서브시스템에서 EJB실행시 사용되는 스레드 풀에 대한 정보를 얻을 수 있다. 또, EJB의 모니터링 항목으로는 빈 인스턴스 풀의 런타임 정보도 중요하다. 이 정보는 배포된 EJB에서 수집할 수 있다. 사용방법은 ‘배포된 애플리케이션 모니터링’에서 설명한다.

  • EJB3의 스레드 풀

‘/subsystem=ejb3/thread-pool=default:read-resource(include-runtime=true)’에서 JBoss EAP 6 EJB3 서브시스템의 스레드 풀 정보를 수집할 수 있다. EJB3의 스레드 풀의 주요 속성값은 다음 표와 같다.

속성 설명

active-count

실행 중인 스레드 수

completed-task-count

실행이 완료한 작업의 수

current-thread-count

풀의 현재 스레드 수

largest-thread-count

현재까지 풀에 있었던 스레드의 최대 개수

max-threads

풀의 최대 스레드 수

rejected-count

거부된 작업의 개수

task-count

지금까지 실행된 태스크의 총 개수

표 6. EJB3 default스레드 풀의 주요 속성값

아래와 같이 CLI로 EJB3의 default 스레드 풀의 런타임 정보를 얻을 수 있다.

[standalone@localhost:9999 /] /subsystem=ejb3/thread-pool=default:read-resource(include-runtime=true)

{
  "outcome" => "success",
  "result" => {
    "active-count" => 0,
    "completed-task-count" => 0L,
    "current-thread-count" => 0,
    "keepalive-time" => {
      "time" => 100L,
      "unit" => "MILLISECONDS"
    },
    "largest-thread-count" => 0,
    "max-threads" => 10,
    "name" => "default",
    "queue-size" => 0,
    "rejected-count" => 0,
    "task-count" => 0L,
    "thread-factory" => undefined
  }
}

트랜잭션

트랜잭션 서브시스템에서는 트랜잭션의 런타임 정보를 얻을 수 있다.

‘/subsystem=transactions:read-resource(include-runtime=true)’에서 JBoss EAP 6 서버의 트랜잭션 서브 시스템의 트랜잭션 정보를 수집할 수 있다. 트랜잭션 정보의 주요 속성값은 다음 표와 같다.

속성 설명

default-timeout

기본 타임아웃값

number-of-aborted-transactions

중지(롤백)된 트랜잭션 수

number-of-application-rollbacks

애플리케이션의 요청에 따라 롤백 된 트랜잭션 수

number-of-committed-transactions

완료된 트랜잭션 수

number-of-heuristics

휴리스틱으로 종료한 트랜잭션 수

number-of-inflight-transactions

시작하여 아직 종료되지 않은 트랜잭션 수

number-of-nested-transactions

중첩된(서브) 트랜잭션 수

number-of-resource-rollbacks

자원의 문제로 롤백 된 트랜잭션 수

number-of-timed-out-transactions

타임아웃 때문에 롤백 된 트랜잭션의 수

number-of-transactions

작성된 트랜잭션의 총 개수(중첩된 트랜잭션 포함)

표 7. 트랜잭션 정보 주요 속성값

다음과 같이 CLI로 트랜잭션의 런타임 정보를 얻을 수 있다.

[standalone@localhost:9999 /] /subsystem=transactions:read-resource(include-runtime=true)

{
  "outcome" => "success",
  "result" => {
    "default-timeout" => 300,
    "enable-statistics" => false,
    "enable-tsm-status" => false,
    "jdbc-action-store-drop-table" => false,
    "jdbc-action-store-table-prefix" => undefined,
    "jdbc-communication-store-drop-table" => false,
    "jdbc-communication-store-table-prefix" => undefined,
    "jdbc-state-store-drop-table" => false,
    "jdbc-state-store-table-prefix" => undefined,
    "jdbc-store-datasource" => undefined,
    "jts" => false,
    "node-identifier" => "1",
    "number-of-aborted-transactions" => 0L,
    "number-of-application-rollbacks" => 0L,
    "number-of-committed-transactions" => 0L,
    "number-of-heuristics" => 0L,
    "number-of-inflight-transactions" => 0L,
    "number-of-nested-transactions" => 0L,
    "number-of-resource-rollbacks" => 0L,
    "number-of-timed-out-transactions" => 0L,
    "number-of-transactions" => 0L,
    "object-store-path" => "tx-object-store",
    "object-store-relative-to" => "jboss.server.data.dir",
    "path" => "var",
    "process-id-socket-max-ports" => 10,
    "process-id-uuid" => true,
    "recovery-listener" => false,
    "relative-to" => "jboss.server.data.dir",
    "socket-binding" => "txn-recovery-environment",
    "status-socket-binding" => "txn-status-manager",
    "use-hornetq-store" => false,
    "use-jdbc-store" => false,
    "log-store" => {"log-store" => undefined}
  }
}

배포된 애플리케이션 모니터링

  • 애플리케이션의 세션 상황

‘/deployment=<애플리케이션명>.war/subsystem=web:read-resource(include-runtime=true)’에서 JBoss EAP 6 서버에 배포된 애플리케이션의 세션 상태 정보를 얻을 수 있다.

속성 설명

active-sessions

사용 중인 세션 수

duplicated-session-ids

랜덤 소스에 의해 생성된 중복 세션 ID의 개수

expired-sessions

타임아웃에 의해 종료된 세션의 수

max-active-sessions

사용중인 세션 수의 최대 수

rejected-sessions

작성 가능한 세션 수 상한에 도달했기 때문에 세션 작성을 거절한 횟수

session-avg-alive-time

세션이 유지된 평균 시간. 단위:초

session-max-alive-time

세션이 유지된 최대 시간. 단위:초

sessions-created

세션 관리자(Manager)가 생성한 세션의 총 개수

표 8. 애플리케이션 세션 상태 주요 속성값

다음과 같이 CLI로 웹 애플리케이션의 세션 상태 런타임 정보를 수집할 수 있다.

[standalone@127.0.0.1:9999 deployment] /deployment=helloejb.war/subsystem=web:read-resource(include-runtime=true)

{
  "outcome" => "success",
  "result" => {
    "active-sessions" => 0,
    "context-root" => "/helloejb",
    "duplicated-session-ids" => 0,
    "expired-sessions" => 0,
    "max-active-sessions" => 0,
    "rejected-sessions" => 0,
    "session-avg-alive-time" => 0,
    "session-max-alive-time" => 0,
    "sessions-created" => 0,
    "virtual-host" => "default-host",
    "servlet" => {"com.opennaru.wstest.Hello" => undefined}
  }
}
  • EJB3의 인스턴스 풀 상황

‘/deployment=<애플리케이션명>.war/ ejb3/stateless-session-bean=<EJB명>:read-resource(include-runtime=true)’에서 JBoss EAP 6 서버에 배포된 EJB의 인스턴스 풀 정보를 얻을 수 있다.

속성 설명

pool-available-count

사용 가능한 인스턴스 풀의 개수

pool-create-count

생성된 인스턴스의 총 개수

pool-current-size

현재 풀의 크기

pool-max-size

풀의 최대 크기

pool-remove-count

풀에서 제거된 인스턴스 개수

표 9. EJB3 인스턴스 풀의 주요 속성값

다음과 같이 CLI로 EJB3의 인스턴스 풀의 런타임 정보를 수집할 수 있다.

[standalone@127.0.0.1:9999 deployment] /deployment=helloejb.war/subsystem=ejb3/stateless-session-bean=HelloStateless:read-resource(include-runtime=true)

{
  "outcome" => "success",
  "result" => {
    "component-class-name" => "HelloStateless",
    "declared-roles" => [],
    "execution-time" => 0L,
    "invocations" => 0L,
    "methods" => {},
    "peak-concurrent-invocations" => 0L,
    "pool-available-count" => 20,
    "pool-create-count" => 0,
    "pool-current-size" => 0,
    "pool-max-size" => 20,
    "pool-name" => "slsb-strict-max-pool",
    "pool-remove-count" => 0,
    "run-as-role" => undefined,
    "security-domain" => "other",
    "timers" => [],
    "wait-time" => 0L
  }
}

19-2.Groovy스크립트를 이용한 모니터링

모니터링에서 현재 시점의 데이터도 중요하지만, 모니터링 항목들의 값을 수집하여 분석하면 시스템의 상황이나 사용량에 대한 추세를 확인할 수 있다. 즉, 사용자가 어떤 시간대에 애플리케이션을 많이 사용하는지, 어떤 시간에 리소스가 부족한지 등 시간에 따라 변화하는 값들을 분석하면 장애 상황을 미리 대처하는 데 도움이 된다.

많은 모니터링 툴들이 있지만, 스크립트나 자바 코딩을 통해 JBoss에서 제공하는 값을 이용하여 파일로 데이터를 남기는 간단한 코딩 방법을 살펴보자.

Groovy는 자바 플랫폼에서 실행할 수 있는 동적 스크립트 언어이다. 자바 언어와 유사하고 기존의 모든 자바 객체와 라이브러리를 그대로 사용할 수 있기 때문에 자바 언어에서 컴파일하지 않고 스크립트 파일로 사용하고 싶을 때 장벽 없이 쉽게 사용할 수 있다.

다음에서 스탠드얼론 모드의 데이터소스 런타임 정보를 수집하는 스크립트와 도메인 모드에서 애플리케이션별 세션의 런타임 정보를 수집하는 스크립트를 설명한다.

이 스크립트 파일은 CLI 명령을 실행하는 것이기 때문에 조금만 수정하면, 앞서 설명한 다양한 모니터링 항목들을 모두 수집할 수 있을 것이다.

데이터소스 정보 수집 스크립트

다음은 스탠드얼론 모드의 데이터소스의 런타임 정보를 주기적으로 남기는 Groovy 스크립트이다. JBoss에서는 JBoss의 CLI 명령을 Java API에서 사용할 수 있도록 jboss-cli-client.jar 파일을 제공하고 있다. Groovy 스크립트에서 CLI API를 사용하여 JBoss에 CLI 명령을 전송하고 결과를 받아 처리할 수 있다.

예제를 실행하려면 Groovy(http://groovy.codehaus.org/)를 다운로드 받아 설치하고, jboss-cli-client.jar 파일을 클래스 패스에 추가해야 한다.

export GROOVY_HOME=$DIRNAME/groovy-2.1.1

export CLASSPATH=$CLASSPATH:/$DIRNAME/lib/jboss-cli-client.jar

export PATH=$PATH:$GROOVY_HOME/bin

다음과 같이 Groovy 스크립트를 실행한다.

groovy standalone_datasource.groovy

스크립트의 결과는 표준 출력(stdout)에 출력되기 때문에 백그라운드 프로세스로 실행하면서 출력을 리다이렉트하면 파일에 결과를 저장해 놓을 수 있다.

nohup groovy -Dhost=${HOST} -Dport=${PORT} scripts/standalone_datasource.groovy >> logs/${HOST}-${PORT}-standalone_datasource.log &

출력 형식은 CSV(Comma Separated Values)이기 때문에 파일을 엑셀에서 불러들여서 그래프를 그려 분석할 수 있다.

다음은 standalone_datasource.groovy 파일의 내용이다.

import org.jboss.as.cli.scriptsupport.*

try {
  cli = CLI.newInstance()
  cli.connect(System.getProperty("host", "localhost").toString(), System.getProperty("port", "9999").toInteger(), null, null)
} catch (e) {
  println("Can't connect to jboss management")
  return
}

println("==============================================================================================================")
println("Time, DataSource, ActiveCount, AvailableCount, AverageBlockingTime, AverageCreationTime, CreatedCount, DestroyedCount, MaxCreationTime, MaxUsedCount, MaxWaitTime, TimedOut, TotalBlockingTime, TotalCreationTime")
println("==============================================================================================================")

while(true) {
  try {
    // ./jboss-cli.sh --controller=localhost:9999 --connect --command="/subsystem=datasources/data-source=MySQLDS/statistics=pool:read-resource(include-runtime=true)"
    result = cli.cmd("/subsystem=datasources/:read-children-names(child-type=data-source)")

    if (result.isSuccess()) {
      for (datasource in result.getResponse().get("result").asList()) {
        print(new Date().format("yyyy-MM-dd kk:mm:ss Z") + ", ")
        print(datasource.asString() + ", ")

        result = cli.cmd("/subsystem=datasources/data-source=" + datasource.asString() + "/statistics=pool:read-resource(include-runtime=true)")

        if (result.isSuccess()) {
          stats = result.getResponse().get("result")
          print( stats.get("ActiveCount").asInt() + ", ")
          print( stats.get("AvailableCount").asInt() + ", ")
          print( stats.get("AverageBlockingTime").asInt() + ", ")
          print( stats.get("AverageCreationTime").asInt() + ", ")
          print( stats.get("CreatedCount").asInt() + ", ")
          print( stats.get("DestroyedCount").asInt() + ", ")
          print( stats.get("MaxCreationTime").asInt() + ", ")
          print( stats.get("MaxUsedCount").asInt() + ", ")
          print( stats.get("MaxWaitTime").asInt() + ", ")
          print( stats.get("TimedOut").asInt() + ", ")
          print( stats.get("TotalBlockingTime").asInt() + ", ")
          print( stats.get("TotalCreationTime").asInt() )
          println( )

        }

      }

    }
    sleep(1000)

  } catch (e) {

    println(" disconnected !!! ")
    sleep(1000)

    try {

      cli.disconnect()

      cli.connect(System.getProperty("host", "localhost").toString(), System.getProperty("port", "9999").toInteger(), null, null)

    } catch (y) {}
  }
}

cli.disconnect();

애플리케이션 세션 정보수집 스크립트

다음은 도메인 모드에서 웹 애플리케이션 세션 정보를 주기적으로 수집하여 출력하는 groovy 스크립트이다.

스탠드얼론 모드에서는 CLI에서 JBoss 인스턴스 하나에 대한 정보만 가져오기 때문에 간단하지만, 도메인 모드에서는 도메인 컨트롤러가 관리하는 호스트의 서버 인스턴스 정보를 수집하여 각 서버의 런타임 정보를 수집하여야 한다.

아래 groovy 스크립트에서 CLI 실행 절차는 다음과 같다.

  1. 먼저 "/:read-children-names(child-type=host)" CLI를 실행하여 호스트 목록을 가져온다.

  2. 해당 호스트마다 CLI 명령을 실행하여 서버 목록을 가져온다( :read-children-names(child-type=server) )

  3. 서버마다 배포된 애플리케이션 정보를 수집한다( :read-children-names(child-type=deployment) )

  4. 애플리케이션의 런타임 세션 정보를 가져온다( subsystem=web:read-resource(include-runtime=true) )

  5. 세션정보를 출력한다.

실행하는 방법은 스탠드얼론 모드에 대한 Groovy 스크립트와 같다. 실행 시 HOST와 PORT 환경변수는 도메인 컨트롤러의 IP와 네이티브 관리 포트를 지정해야 한다.

export HOST=192.168.0.101

export PORT=9999

nohup groovy -Dhost=${HOST} -Dport=${PORT} scripts/domain_session.groovy >> logs/${HOST}-${PORT}-domain_session.log &

위와 같이 실행하면 파일이 생성되기 때문에, 이 파일을 엑셀에서 분석하여 시간별, 애플리케이션 별 세션의 상황을 모니터링 할 수 있다.

다음은 domain_session.groovy 스크립트 파일이다.

import org.jboss.as.cli.scriptsupport.*

try {

  cli = CLI.newInstance()
  cli.connect(System.getProperty("host", "localhost").toString(), System.getProperty("port", "9999").toInteger(), null, null)

} catch (e) {

  println("Can't connect to jboss management")

  return

}

println("==============================================================================================================")

println("Time, Host, Server, Application, active-sessions, context-root, duplicated-session-ids, expired-sessions, max-active-sessions, rejected-sessions, session-avg-alive-time, session-max-alive-time, sessions-created, virtual-host")

println("==============================================================================================================")

while(true) {
  try {
    // ./jboss-cli.sh --controller=localhost:9999 --connect --command="/deployment=session.war/subsystem=web:read-resource(include-runtime=true)"
    result = cli.cmd("/:read-children-names(child-type=host)")
    response = result.getResponse()

    for(host in response.get("result").asList()) {
      result = cli.cmd("/host=" + host.asString() + "/:read-children-names(child-type=server)")

      if (result.isSuccess()) {
        for (server in result.getResponse().get("result").asList()) {
          result = cli.cmd("/host=" + host.asString() + "/server=" + server.asString() + ":read-children-names(child-type=deployment)")

          if (result.isSuccess()) {
            for (application in result.getResponse().get("result").asList()) {
              print(new Date().format("yyyy-MM-dd kk:mm:ss Z") + ", ")
              print(host.asString() + ", ")
              print(server.asString() + ", ")
              print(application.asString() + ", ")
              result = cli.cmd("/host=" + host.asString() + "/server=" + server.asString() +

              "/deployment=" + application.asString() + "/subsystem=web:read-resource(include-runtime=true)")

              if (result.isSuccess()) {
                stats = result.getResponse().get("result")
                print( stats.get("active-sessions").asInt() + ", ")
                print( stats.get("context-root").asString() + ", ")
                print( stats.get("duplicated-session-ids").asInt() + ", ")
                print( stats.get("expired-sessions").asInt() + ", ")
                print( stats.get("max-active-sessions").asInt() + ", ")
                print( stats.get("rejected-sessions").asInt() + ", ")
                print( stats.get("session-avg-alive-time").asInt() + ", ")
                print( stats.get("session-max-alive-time").asInt() + ", ")
                print( stats.get("sessions-created").asInt() + ", ")
                print( stats.get("virtual-host").asString() )
                println( )
              }
            }
          }
        }
      }
    }
    sleep(1000)

  } catch (e) {
    println(" disconnected !!! ")
    e.printStackTrace()
    sleep(1000)

    try {
      cli.disconnect()
      cli.connect(System.getProperty("host", "localhost").toString(), System.getProperty("port", "9999").toInteger(), null, null)
    } catch (y) {}
  }
}

cli.disconnect();

Java 코드에서 CLI 명령어 사용하기

Groovy는 Java의 모든 객체와 라이브러리를 그대로 사용할 수 있다고 설명하였다. 스크립트 실행에 필요한 jboss-cli-client.jar 파일은 원래 Java에서 CLI API를 사용하기 위해 만들어진 것이다. Java 코드에서 아래와 같이 JBoss 컨트롤러에 접속하여 CLI 명령을 실행할 수 있다.

코드의 주요 내용은 다음과 같다.

  • CLI를 실행할 CommandContext 인스턴스를 생성한다.

CommandContext ctx = CommandContextFactory.getInstance();
  • 컨트롤러에 접속한다.

ctx.connectController("192.168.0.101", 9999);
  • CLI명령을 실행한다.

ctx.handle(“cd /”);
  • CLI 오퍼레이션을 실행한다.

ModelNode hostname = ctx.buildRequest(":read-children-names(child-type=host)");

다음의 코드는 CLI로 도메인 컨트롤러에 접속하여 호스트의 목록을 가져와 출력하는 것이다.

package com.khan.jbosscli.test;
import java.util.Iterator;
import java.util.List;
import org.jboss.as.cli.CliInitializationException;
import org.jboss.as.cli.CommandContext;
import org.jboss.as.cli.CommandContextFactory;
import org.jboss.as.cli.CommandLineException;
import org.jboss.dmr.ModelNode;

public class TestCLI {
  public static void main(String[] args) {
    // Initialize the CLI context
    final CommandContext ctx;

    try {
      ctx = CommandContextFactory.getInstance().newCommandContext("admin", "opennaru!234".toCharArray());
    } catch(CliInitializationException e) {
      throw new IllegalStateException("Failed to initialize CLI context", e);
    }

    try {
      // connect to the server controller
      ctx.connectController("192.168.0.101", 9999);
      // execute commands and operations
      ctx.handle("cd /");

      ModelNode hostname = ctx.buildRequest(":read-children-names(child-type=host)");
      List<ModelNode> list = hostname.asList();
      Iterator i = list.iterator();

      while( i.hasNext() ) {
        ModelNode a = (ModelNode) i.next();
        System.out.println( a.toString() );
      }
    } catch (CommandLineException e) {
      // the operation or the command has failed
    } finally {
      // terminate the session and
      // close the connection to the controller

      ctx.terminateSession();
      ctx.disconnectController();
    }
  }
}

19-3.JMX 모니터링

JMX는 Java Management eXtensions의 약자로 자바 기반의 모든 애플리케이션을 모니터링하기 위해 만든 표준 기술이다. JDK에서 지원하는 표준이다. Java EE 애플리케이션 서버들은 JMX로 모니터링할 수 있도록 서버의 데이터 값들을 MBean(Management Bean) 형태로 제공하고 있다.

여기서 MBean은 JMX에서 모니터링 값을 제공하기 위한 빈이다. MBean의 종류는 표준 MBean, 동적 MBean, 모델 MBean, 오픈 MBean로 4가지가 있다. MBean들은 데이터값과 오퍼레이션(메서드)을 제공하기 때문에 JMX를 통해 모니터링 값을 가져오고 메서드도 실행할 수 있다.

JConsole

MBean을 모니터링하기 위한 jconsole이라는 툴을 JDK에서 제공하고 있다. JBoss EAP 6에서는 MBean과 JVM에 대한 모니터링뿐만 아니라 CLI 명령을 실행할 수 있도록 JConsole을 확장하였다. $JBOSS_HOME/bin/jconsole.sh 파일이 JBoss에서 확장한 JConsole을 실행하는 스크립트이다.

연결방법

실행된 jboss-module.jar 로 시작하는 프로세스를 클릭하면 JBoss 인스턴스에 접속된다.

그림 1. JConsole 초기화면

Remote Process는 URI을 입력하여 원격의 JBoss 인스턴스에 접속할 수 있다. 여기서 URI는 Java에서 기본적으로 제공하는 방식과 다른 JBoss URI를 입력해야 한다.

JBoss EAP 6에서는 RMI를 사용하지 않고 NIO(Non Blocking IO)를 사용한 remoting을 사용한다. NIO를 사용하기 때문에 네트워크 데이터 전송 및 처리 속도가 RMI보다 훨씬 빠르다. JMX연결에도 remoting을 사용하기 위하여, remoting-jmx라는 프로토콜을 제공하고 있다.

원격의 JBoss EAP 6에 연결하려면 URI로 ‘service:jmx:remoting-jmx://192.168.0.11:9999’와 같이 입력하고 연결하면 된다. 여기서 9999 포트는 스탠드얼론 모드 서버의 관리 포트이다.

service:jmx:remoting-jmx://192.168.0.11:9999

도메인 모드일 경우에는 접속방법이 다르다. 도메인 컨트롤러에 접속하는 것이 아니다. 도메인 모드에서 JMX 정보는 도메인 컨트롤러가 가지고 있지 않다. 각각의 JBoss 인스턴스들이 JMX 정보를 개별적으로 제공하기 때문에, 개별 인스턴스의 Remoting 포트로 접속해야 한다. 아래와 같이 서버 인스턴스의 포트 오프셋 0일 경우 4447포트로 접속한다. 오프셋 100인 인스턴스에 접속하려면 4547 포트로 접속하면 된다.

service:jmx:remoting-jmx://192.168.0.11:4447

또, 도메인 모드에서 JMX를 사용하려면 domain.xml 파일에서 JMX 서브시스템의 use-management-endpoint를 false로 설정을 변경하여야 한다.

<subsystem xmlns="urn:jboss:domain:jmx:1.3">

<expose-resolved-model/>

<expose-expression-model/>

<remoting-connector use-management-endpoint="false"/>

JConsole을 사용하여 서버에 접속하면 다음과 같이 Java의 메모리 정보를 그래프로 출력한다. 탭을 변경하여 메모리, 스레드, 클래스 로딩상황, MBean 정보들을 확인할 수 있다.

그림 2. JConsole로 접속화면

MBean이 제공하는 메서드를 호출할 수 있다.

그림 3. MBean 모니터링 화면

Java 코드에서 MBean 호출 방법

CLI를 호출하여 데이터를 파일로 저장하여 분석했던 것처럼, JMX가 제공하는 MBean 값들을 수집하여 주기적으로 저장하면 분석에 활용할 수 있을 것이다. JBoss에서는 일반적인 Java의 RMI가 아닌 remoting-jmx를 사용하기 때문에 다음과 같은 코드로 JBoss EAP 6의 MBean 값을 가져올 수 있다.

// Get a connection to the JBoss EAP MBean server on localhost

String host = "localhost";

int port = 4447; // remoting-jmx port

String urlString = System.getProperty("jmx.service.url", "service:jmx:remoting-jmx://" + host + ":" + port);

JMXServiceURL serviceURL = new JMXServiceURL(urlString);

Hashtable h = new Hashtable();

String[] credentials = new String[] { "admin", "opennaru!234" };

h.put("jmx.remote.credentials", credentials);

JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceURL, h);

MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();

Hashtable<String, String> table = new Hashtable<String, String>();

table.put("subsystem", "messaging");

table.put("hornetq-server", "default");

ObjectName name = ObjectName.getInstance("jboss.as", table);

Boolean started = (Boolean) connection.getAttribute(name, "started");

System.out.println(started);

jmxConnector.close();