top bar

글 목록

2015년 9월 23일 수요일

[Performance] VisualVM + JMX 연동을 통한 tomcat cpu 사용량 모니터링

 지난 번에는 VisualVM과 Jstatd를 연동하여 remote jvm 을 모니터링하는 방법을 살펴보았다. 하지만 위 방법은 원격지 서버의 cpu 사용률까지 모니터링 하지는 못한다는 단점이 있었다.

이번 포스팅 에서는 'JMX'를 이용하여 원격지 jvm의 cpu사용량을 모니터링 해보도록 하겠다.

JMX란?



 JMX(Java Management Extensions)는 프로그래머들에게 자바 어플리케이션의 모니터링과 관리 기능을 제공한다. 실제로 이 API는 웹서버에서 네트워크 디바이스, 웹폰에 이르기까지 자바로 이용 가능한 것은 어느 것이든 로컬 혹은 원격으로 처리 할 수 있게 한다. JMX 기술은 JCP(Java Community Process)에 의해 개발된 밀접한 관계의 두 스펙, Java Specification Request (JSR) 3: Java Management Extensions (JMX) Specification 와 JSR 160: Java Management Extensions (JMX) Remote API 1.0에 의해 정의된다.


톰캣 구동 스크립트에 JVM 옵션 추가



매우 간단하다.

먼저 톰캣서버를 구동하기전에, bin/catalina.sh 쉘스크립트 파일에 아래와 같이 jvm 옵션을 추가해야 한다.
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=[port] -Djava.rmi.server.hostname=[ip address]
해당 옵션을 추가한후에 톰캣을 구동한다.


VisualVM 에서 모니터링하기



1) Remote > Add Remote Host...















입력창이 뜨면 원격지의 IP를 입력한다

2) Add JMX Connection

















3) port 입력















jvm 옵션 추가시 입력했던 port를 입력한뒤 OK를 누르면 아래와 같이 생성된다


4) Sampler 탭에서 모니터링

이제 아래와 같이 원격지 tomcat 서버 전체의 cpu 사용량과 
각 클래스 및 메소드의 cpu사용량을 확인 할 수 있다!


2015년 9월 3일 목요일

[Elasticsearch] Logstash를 이용한 bulk 데이터 인덱싱

들어가며..



이전 포스트에서 Logstash와 Elasticsearch를 연동하여 인덱싱하는 방법을 살펴보았다. 참고

오늘은 bulk format의 데이터를 Logstash를 이용해 인덱싱하는 방법을 정리하겠다. 하지만 기본적으로 Logstash는 bulk format을 처리하지 못한다. 왜 그런지 아래 예제를 보며 살펴보겠다.

일단 로그스태쉬 configuration은 아래와 같이 작성한다.
input {
        file {
                codec => json
                path => "/home/asuraiv/logstash_data/*.json"
        }
}

output {
        elasticsearch {
                host => "localhost"
                index => "school"
                index_type => "students"
                protocol => "http"
                template_name => "school"
        }
        stdout {
                codec => rubydebug {
                        metadata => true
                }
        }
}
input plugin은 'file'을 사용하였다. 'path' 값으로 설정된 경로안에 json 확장자 파일이 저장되면 Logstash의 file input plugin이 자동으로 그 파일을 읽어 output으로 보내준다.

아래와 같은 bulk format을 저장하여 인덱싱을 시도해보자
{ "index" : { "_index" : "school", "_type" : "students", "_id" : "1" }}
{ "address" : "newyork", "name" : "james", "class" : "A" }
하지만 결과는 아래와 같다
{
         "index" => {
        "_index" => "school",
         "_type" => "students",
           "_id" => "1"
    },
      "@version" => "1",
    "@timestamp" => "2015-09-03T08:03:14.064Z",
          "host" => "cweb02.ami",
          "path" => "/home1/irteam/logstash_data/data.json",
     "@metadata" => {
        "retry_count" => 0
    }
}
{
       "address" => "los engeles",
          "name" => "kim",
         "class" => "C",
      "@version" => "1",
    "@timestamp" => "2015-09-03T08:03:14.065Z",
          "host" => "cweb02.ami",
          "path" => "/home1/irteam/logstash_data/data.json",
     "@metadata" => {
        "retry_count" => 0
    }
}
원래 bulk format은 첫번째줄이 meta정보, 두번째줄이 source 데이터이다. 따라서 2줄이 하나의 event로 인식되야 하는것인데 지금은 각 라인이 서로 다른 event로 인식되어 따로따로 인덱싱이 되었다.

bulk format에서 첫째줄 메타정보에 인덱싱을 수행할 인덱스, 타입, id값등을 설정할 수 있지만 위와같은 결과가 나오면 이 메타정보가 아무 소용이 없다. 두번째 줄의 데이터가 인덱싱이 된것도 Logstash configuration에 'index'와 'index_type'이 명시되어있기 때문에 정상적으로 인덱싱이 된것이다.

따라서 bulk format을 정상적으로 인지하면서, 메타정보에 맞게 유동적으로 index, type, id등을 설정하여 인덱싱을 하려면 어떻게 해야할까?

es_bulk codec



input 단계에서 bulk format을 파싱하여 메타데이터와 source데이터를 하나의 이벤트로 묶어주는 codec이 있다.

Logstash 설치 폴더의 'bin' 에서 아래와 같은 명령어로 설치가 가능하다 (Logstash 1.4+)
$ ./plugin install logstash-codec-es_bulk
그리고 configuration 파일의 input 설정부분에 아래와 같이 작성한다
input {
        file {
                path => "/home/asuraiv/logstash_data/*.json"
                codec => es_bulk {
                }
        }
}
위의 하이라이트된 설정을 추가한다. es_bulk 코덱안에는 별다른 설정이 없는 그냥 비어있는 중괄호 { } 이다.

이 코덱을 적용하면 bulk format의 메타데이터 부분은 '@metadata' 로 넣어주고, filter나 output plugin에서 "{[@metadata][_index]}" 와 같은 방식으로 접근할 수 있다. 따라서, output pulgin을 아래와 같이 작성하면 bulk format에 메타데이터에 따라 유동적으로 인덱싱할 인덱스, 타입, id를 지정할 수 있다
elasticsearch {
        host => "localhost"
        index => "%{[@metadata][_index]}"
        index_type => "%{[@metadata][_type]}"
        document_id => "%{[@metadata][_id]}"
        protocol => "http"
        template_name => "school"
}



2015년 9월 2일 수요일

[JAVA] Set

개요



Set은 각 객체 값에 대해 하나의 인스턴스만 저장한다. 즉, 동일한 객체의 인스턴스를 추가하려고 하면 중복을 막는다. Set의 가장 일반적인 용도는 특정 객체의 존재 유무를 확인하는 것이고 때문에 contain 메소드를 사용한 '검색'이 Set에서 가장 중요한 기능이다. 일반적으로 해싱함수를 사용한 HashSet이 가장 많이 쓰인다.


예제



아래는 Integer 객체를 저장하는 HashSet 사용 예이다.
Random rand = new Random(47);

Set<Integer> intset = new HashSet<Integer>();
for (int i = 0; i < 10000; i++) {
    intset.add(rand.nextInt(30));
}
System.out.println(intset);
> 결과
[0, 1, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 12, 15, 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28]
0부터 29 사이의 값에 대해서 10000개의 무작위 수가 Set에 추가되었지만 위에서 보는바와 같이 중복을 허용하지 않고 있다.

만약 정렬된 결과를 얻고 싶다면 TreeSet을 사용한다.
SortedSet<Integer> intset = new TreeSet<Integer>();

for (int i = 0; i < 10000; i++) {
    intset.add(rand.nextInt(30));
}
System.out.println(intset);
> 결과
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
TreeSet의 요소들은 red-black tree(2진 정렬 트리)의 구조로 데이터 정렬을 유지하여 위와같은 결과가 나온다

LinkedHashSet도 정렬을 유지한다. 하지만 정렬의 기준은 '추가된 순서' 이다. 아래와 같다.
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<Integer>();
        
linkedHashSet.add(1);
linkedHashSet.add(10);
linkedHashSet.add(5);
linkedHashSet.add(3);
linkedHashSet.add(7);
        
System.out.println(linkedHashSet);  
> 결과
[1, 10, 5, 3, 7]


2015년 9월 1일 화요일

[JAVA] List - LinkedList

List의 구현체중 가장 많이 쓰이는 'ArrayList'에 대해서는 이전 포스트에 정리했다. 참고
오늘은 LinkedList에 대해서 정리할텐데, 이는 우리가 아주 잘 아는 자료구조 중 하나이다. 가장 일반적인 '단방향 LinkedList'의 경우, 하나의 노드의 포인터가 다음노드를 가리키는 식으로 되어있어 연결된(Linked) 구조를 갖는다.

예제 - LinkedList



(1) getFirtst , element , peek
LinkedList<Pet> pets = new LinkedList<Pet>(Pets.arrayList(5));

System.out.println(pets);   
    
System.out.println("pets.getFirst() : " + pets.getFirst());
System.out.println("pets.element() : " + pets.element());
System.out.println("pets.peek() : " + pets.peek());
> 결과
[Rat, Manx, Cymric, Mutt, Pug]
pets.getFirst() : Rat
pets.element() : Rat
pets.peek() : Rat
일단 이 세 메소드는 동일하다. 가장 첫번째 요소를 반환한다. 단지 element() 메소드는 getFirst() 메소드의 'alias' 일뿐이고, peek() 메소드는 리스트가 비어있을 때 null을 반환한다.

(2)  remove , removeFirst , poll
LinkedList<Pet> pets = new LinkedList<Pet>(Pets.arrayList(5));

System.out.println("pets.remove() : " + pets.remove());
System.out.println("pets.removeFirst() : " + pets.removeFirst());
        
System.out.println("pets : " + pets);

System.out.println("pets.poll() : " + pets.poll());
System.out.println("After pets.poll() : " + pets);
> 결과
[Rat, Manx, Cymric, Mutt, Pug]
pets.remove() : Rat
pets.removeFirst() : Manx
pets : [Cymric, Mutt, Pug]
pets.poll() : Cymric
After pets.poll() : [Mutt, Pug]
세 메소드 역시 동일하며 remove()는 removeFirst()의 alias이다. 가장 첫번째 요소를 삭제하면서 반환한다. poll() 메소드는 리스트가 비어있을때 null을 반환한다.

(3) addFirst , offer , add , addLast
LinkedList<Pet> pets = new LinkedList<Pet>(Pets.arrayList(5));
        
System.out.println(pets);

pets.addFirst(new Rat());       
System.out.println("After addFirst() : " + pets);
    
pets.offer(Pets.randomPet());   
System.out.println("After offer() : " + pets);
    
pets.add(Pets.randomPet()); 
System.out.println("After add() : " + pets);

pets.addLast(new Hamster());    
System.out.println("After addLast() : " + pets);
> 결과
[Rat, Manx, Cymric, Mutt, Pug]
After addFirst() : [Rat, Rat, Manx, Cymric, Mutt, Pug]
After offer() : [Rat, Rat, Manx, Cymric, Mutt, Pug, Cymric]
After add() : [Rat, Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug]
After addLast() : [Rat, Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Hamster]
요소를 추가하는 메소드들이다. addFirst() 는 리스트의 맨앞에 요소를 추가하며, 나머지 세 메소드 offer(), add(), addLast()는 리스트의 끝에 요소를 추가한다.

(4) remove
LinkedList<Pet> pets = new LinkedList<Pet>(Pets.arrayList(5));
        
System.out.println(pets);

System.out.println("pets.removeLast() : " + pets.removeLast());
System.out.println("After removeLast() : " + pets);
>결과
[Rat, Manx, Cymric, Mutt, Pug]
pets.removeLast() : Pug
After removeLast() : [Rat, Manx, Cymric, Mutt]
remove() 메소드는 리스트의 맨끝 요소를 반환하며 삭제한다.