핫 스레드 API는 여러 정보를 포함한 형태를 가진 텍스트로 리턴한다. 즉 JSON 구조로 리턴하지 않는 형태를 갖고 있다. 


응답 구조 자체에 대해 설명하기 전에 핫 스레드 API의 응답을 생성하는 로직을 짧게 소개한다.


일래스틱서치는 먼저 실행 중인 모든 스레드를 얻은 후 각 스레드에서 소비한 CPU 시간, 특정 스레드가 차단되었거나 대기 상태에 있었던 횟수, 차단된 시간 또는 대기 상태에 있었던 시간 등에 대한 다양한 정보를 수집한다. 


다음에는 특정 시간(interval 매개 변수로 지정) 동안 기다린 후 시간이 지나면 동일한 정보를 다시 수집한다.


이 작업이 완료되면 각 특정 스레드가 실행되고 있는 시간에 따라 스레드가 정렬된다. 가장 오랜 기간 실행 중인 스레드가 목록 맨 위에 오도록 내림차순으로 정렬된다.



(이전에 언급된 시간은 type 매개 변수에 지정된 오퍼레이션 타입을 기반으로 측정된다. )


그 다음 일래스틱서치는 첫 번째 N개의 스레드(N은 threads 매개 변수로 지정된 스레드 개수)를 분석한다. 


일래스틱서치는 몇 밀리 초마다 이전 단계에서 선택한 스레드의 스택 트레이스(stack trace)의 일부 스냅샷(스냅 샷 수는 스냅 샷 매개 변수로 지정)을 사용한다. 


마지막으로 해야 할 일은 스레드 상태의 변경을 시각화하고, 호출 함수에게 응답을 리턴하기 위해 스택 트레이스를 그룹핑하는 것이다.




threads 개수는 기본 3개이고 간격은 500ms이며 type의 기본 값은 cpu이다. 

간단한 예제를 보면 다음과 같다.

$ curl 'localhost:9200/_nodes/hot_threads?type=wait&interval=1s'
::: {5OEGj_a}{5OEGj_avT8un0nOak28qQg}{DAzM0ktKQNS047ggd9nYZQ}{127.0.0.1}{127.0.0.1:9300}
   Hot threads at 2017-07-31T11:04:59.943Z, interval=1s, busiestThreads=3, ignoreIdleThreads=true:

    8.4% (35.1ms out of 1000ms) cpu usage by thread 'elasticsearch[kemi][search][T#2]'
     10/10 snapshots sharing following 8 elements
       sun.misc.Unsafe.park(Native Method)
       java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
       java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
       java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)
       java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)
       java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231)
       org.elasticsearch.bootstrap.Bootstrap$1.run(Bootstrap.java:84)
       java.lang.Thread.run(Thread.java:745)

....



결과의 첫 부분을 보면..


핫 스레드 API 정보를 리턴하는 노드가 어느 노드인지 쉽게 알 수 있고 핫 스레드 API 호출이 언제 많은 노드로 전달되는 시점을 알 수 있다.





두 번째 부분은 


8.4% (35.1ms out of 1000ms) cpu usage by thread 'elasticsearch[kemi][search][T#2]'


해당 스레드는 측정이 완료된 시점의 모든 CPU 시간 중 8.4%를 차지함을 알 수 있다. 

cpu usage 부분은 cpu와 동일한 type을 사용하고 있음을 나타낸다 (여기에서 예상할 수 있는 다른 값은 블럭(block) 상태에 있는 스레드의 블럭 사용량(block usage)와 대기 상태에 있는 스레드의 대기 사용량(wait usage)이다).  스레드 이름은 여기에서 매우 중요하다. 

스레드를 살펴보면 해당 일래스틱서치 스레드가 가장 핫한 스레드임을 알 수 있다. 이 예제의 핫 스레드가 모두 검색(search 값)이라는 것을 알 수 있다. 

볼 수 있는 다른 값으로는 recovery_stream(복구 모듈 이벤트), cache(이벤트 캐시), merge(세그먼트 병합), index(데이터 저장 스레드) 등이 있다.



관련 내용은 다음 코드를 확인한다.


https://github.com/elastic/elasticsearch/blob/v5.2.1/core/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequest.java



public class NodesHotThreadsRequest extends BaseNodesRequest<NodesHotThreadsRequest> {


    int threads = 3;

    String type = "cpu";

    TimeValue interval = new TimeValue(500, TimeUnit.MILLISECONDS);

    int snapshots = 10;

    boolean ignoreIdleThreads = true;


    // for serialization

    public NodesHotThreadsRequest() {


    }


    /**

     * Get hot threads from nodes based on the nodes ids specified. If none are passed, hot

     * threads for all nodes is used.

     */

    public NodesHotThreadsRequest(String... nodesIds) {

        super(nodesIds);

    }


    public int threads() {

        return this.threads;

    }


    public NodesHotThreadsRequest threads(int threads) {

        this.threads = threads;

        return this;

    }


    public boolean ignoreIdleThreads() {

        return this.ignoreIdleThreads;

    }


    public NodesHotThreadsRequest ignoreIdleThreads(boolean ignoreIdleThreads) {

        this.ignoreIdleThreads = ignoreIdleThreads;

        return this;

    }


    public NodesHotThreadsRequest type(String type) {

        this.type = type;

        return this;

    }


    public String type() {

        return this.type;

    }


    public NodesHotThreadsRequest interval(TimeValue interval) {

        this.interval = interval;

        return this;

    }


    public TimeValue interval() {

        return this.interval;

    }


    public int snapshots() {

        return this.snapshots;

    }


    public NodesHotThreadsRequest snapshots(int snapshots) {

        this.snapshots = snapshots;

        return this;

    }


    @Override

    public void readFrom(StreamInput in) throws IOException {

        super.readFrom(in);

        threads = in.readInt();

        ignoreIdleThreads = in.readBoolean();

        type = in.readString();

        interval = new TimeValue(in);

        snapshots = in.readInt();

    }


    @Override

    public void writeTo(StreamOutput out) throws IOException {

        super.writeTo(out);

        out.writeInt(threads);

        out.writeBoolean(ignoreIdleThreads);

        out.writeString(type);

        interval.writeTo(out);

        out.writeInt(snapshots);

    }

}


Posted by '김용환'
,