Scouter-Pulse를 이용하여 나만의 agent 만들기

Scouter를 사용하여 간단한 agent를 만들어보도록 하겠습니다.
(현재 Scouter는 Java agent와 Host agent 만 개발되어 있습니다.)

원래 scouter의 agent는 고속의 전용 protocol을 사용합니다.
이러한 전용 agent를 개발하기 위해서는 agent의 구조뿐 아니라 collector server의 구조를 이해하고 기능을 추가 개발하여야 하며 또한 Client 화면 까지 맞춰서 개발하여야 합니다.

이는 agent를 개발하는데 상당한 진입장벽이기에 scouter v0.4.18 부터는 숫자로 이루어진 성능 메트릭에 대해서는 간단히 추가할 수 있는 PULSE라는 이름의 새로운 agent i/f방식을 제공하게 되었습니다.

Pulse는 HTTP + JSON 프로토콜을 사용하므로 수집할 정보를 Scouter로 전송해 주면 collector 서버는 자동으로 agent정보를 인식하고 client에 보여지게 됩니다.

또한 java가 아니더라도 프로토콜만 맞추어 주면 되니까 다양한 언어로 제작이 가능하겠죠.

다만 전용 agent에 비해 오버헤드가 클수 밖에 없는 구조이니 이는 감안하여 사용해야 할 듯 합니다.
또한 숫자로 이루어진 성능 메트릭만 수집이 가능합니다.
(예를 들면 cpu:80%, memory:1.8Gb  이런식의 데이터를 말합니다.)

엔드포인트

scouter-pulse는 오브젝트타입 등록, 성능정보(성능 counter) 전송의 두가지 엔드포인트를 제공합니다.

 – http://server_ip:6180/register (오브젝트 타입 등록)

(1) object 의 type을 지정합니다.
(임의의 명칭을 사용하면 됩니다. 예를들어 redis-pulse를 만든다면 redis-p 정도로 정하면 됩니다. 그냥 redis라고 하면 향후 만들어질 redis 전용 agent와 이름이 충돌할 수 있으므로 redis-p 정도를 권장합니다.)

(2) 해당 object가 가지는 성능카운터를 정의합니다.

  {
      "object" : {
          "type" : "test_type",
          "display" : "DisplayName"
      },
      "counters" : [
          {"name" : "counter1",
           "unit" : "cnt",
           "display" : "Counter1",
          },
          {"name" : "counter2",
           "unit" : "cnt",
           "display" : "Counter2",
          },
          {"name" : "counter3",
           "unit" : "cnt",
           "display" : "Counter3",
           "total" : false
          },
      ]
  }

 

 – http://server_ip:6180/counter (성능 정보 전송)

(1) object 정보를 전송합니다.
host는 scouter 클라이언트의 오브젝트뷰에서 보여지는 host type object의 정보입니다.
scouter 클라이언트에서는 host object 하위에 non-host type object가 트리 형태로 달리게 됩니다.
만약 host 이름을 GunMac.local 이라고 지정하였다면, 아래 그림과 같이 클라이언트 화면에서 해당 object(아래 그림에서는 tomcat type 오브젝트 2개)가 GunMac.local 밑에 달리게 됩니다.

%e1%84%89%e1%85%b3%e1%84%8f%e1%85%b3%e1%84%85%e1%85%b5%e1%86%ab%e1%84%89%e1%85%a3%e1%86%ba-2016-09-07-%e1%84%8b%e1%85%a9%e1%84%92%e1%85%ae-10-39-32

(이러한 특성을 이용해서  host의 개념을 그루핑 용도로 사용도 가능합니다. 이건 나중에 예제로 확인해 보도록 하겠습니다.)

(2) 해당 object가 가지는 성능카운터의 값.

  [
       {
          "object" : {
            "host" : "GunMac.local",
              "name" : "name1",
              "type" : "test_type",
              "address" : "10.10.10.10"
          },
          "counters" : [
              {"name" : "counter1", "value" : 55},
              {"name" : "counter2", "value" : 245},
              {"name" : "counter3", "value" : 4245}
          ]
       },
       {
          "object" : {
              "host" : "host2",
              "name" : "name2",
              "type" : "type_name",
              "address" : "10.10.10.11"
          },
          "counters" : [
              {"name" : "counter1", "value" : 35},
              {"name" : "counter2", "value" : 65},
              {"name" : "counter3", "value" : 8888}
          ]
       }
   ]

PULSE를 이용하여 여러가지 솔루션의 성능 정보를 전송할 수도 있겠지만
여기서는 PULSE 구조의 이해를 돕기위해 비즈니스 데이터를 전송하는 샘플을 구현해 보도록 하겠습니다.

이에 대한 전체 소스는 github에서 참고하시면 됩니다.
https://github.com/scouter-project/scouter-pulse/tree/master/pulse.sample.bizcounter/java-sample

아.. 그리고 그전에 collector server에서 pulse 정보를 수신할 수 있도록 설정하여야 합니다.

net_http_server_enabled=true

 

홈쇼핑 비즈니스 모니터링

홈쇼핑의 몇가지 비즈니스 데이터를 전송하는 것으로 가정하겠습니다.
비즈니스 데이터는 수초 간격으로 DB에서 수집을 할수 있을 것이며 이를 Scouter로 전송합니다.

object type은 scouterbizsample1 이라고 지었습니다.
– DisplayName은 “Biz Sample1”
Biz Sample 1 타입의 오브젝트가 제공하는 성능 카운터는 아래와 같습니다.
(1) 분당 주문량 – OPM
(2) 일일 주문량 = DayOrder
(3) 생방송상품 분당 주문량 – OnAirOPM
(4) 생방송상품 분당 주문취소량 – OnAirAPM
(5) 생방송상품 주문성공률 – OnAirOSR
–> 그냥 가정입니다.(이런 지표가 진짜 있는지 저는 모릅니다. ^^ )

아래는 구현한 소스입니다.
pulse http 프로토콜을 맞춘 java library가 있으므로 이것을 활용하였습니다.
(https://github.com/scouter-project/scouter-pulse/releases/tag/v0.1.0-pulse.lib.http)


package scouterx.pulse.sample.bizcounter;
import scouterx.pulse.common.http.HttpTrain;
import scouterx.pulse.common.protocol.register.CounterDef;
import scouterx.pulse.common.protocol.register.ObjectDef;
import scouterx.pulse.common.protocol.register.RegisterBean;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author Gun Lee (gunlee01@gmail.com) on 2016. 7. 30.
*/
public class BizSampleAgent {
public static final String OBJECT_TYPE = "scouterbizsample1";
public static final String COUNTER_OPM = "OPM";
public static final String COUNTER_DAYORDER = "DayOrder";
public static final String COUNTER_ONAIROPM = "OnAirOPM";
public static final String COUNTER_ONAIRAPM = "OnAirAPM";
public static final String COUNTER_ONAIROSR = "OnAirOSR";
static BizSampleAgent instance = new BizSampleAgent();
private BizSampleAgent() {}
public static BizSampleAgent getInstance() {
return instance;
}
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(
(r) -> {
Thread t = new Thread(r, "BizSampleAgentScheduledThread");
t.setDaemon(true);
return t;
}
);
public void start() {
executor.scheduleAtFixedRate(new RegisterTask(), 500, 5*60*1000, TimeUnit.MILLISECONDS);
executor.scheduleAtFixedRate(new BizSendTask(), 4000, 2000, TimeUnit.MILLISECONDS);
}
private class RegisterTask implements Runnable {
@Override
public void run() {
RegisterBean rbean = new RegisterBean.Builder()
.setObjectSpec(new ObjectDef(OBJECT_TYPE, "Biz Sample 1"))
.addCounterSpec(new CounterDef(COUNTER_OPM, "o/m", "order/minute", true, true))
.addCounterSpec(new CounterDef(COUNTER_DAYORDER, "ea", "Daily Order Count", true, true))
.addCounterSpec(new CounterDef(COUNTER_ONAIROPM, "o/m", "On Air order/minute", true, true))
.addCounterSpec(new CounterDef(COUNTER_ONAIRAPM, "o/m", "On Air abandon/minute", true, true))
.addCounterSpec(new CounterDef(COUNTER_ONAIROSR, "%", "On Air order success rate", true, true))
.build();
HttpTrain.getInstance().putRegisterBean(rbean);
}
}
private class BizSendTask implements Runnable {
@Override
public void run() {
HttpTrain.getInstance().putObjectCounterBeans(new BizDataController().getRealtimeOrderInfoAsObjectCounterBean());
}
}
}

이게 소스의 전부입니다.(비즈니스 데이터를 조회하는 부분을 제외한…)

object의 정보 등록은 5분마다 호출되고, 성능 정보는 2초간격으로 송신하도록 하였습니다.

RegisterBean.Builder() 는 object를 등록하는 요청정보를 만드는 부분입니다.

BizDataController().getRealtimeOrderInfoAsObjectCounterBean() 은 현시점의 비즈니스 데이터를 조회하는 로직을 호출합니다.
그리고 HttpTrain.getInstance().putObjectCounterBeans(List<ObjectCounterBean>)를 호출하여 성능카운터 정보를 송신합니다.

결과적으로 Scouter 클라이언트에는 아래와 같이 보여지게 됩니다.
개발환경을 구성해 소스로 실행해 보기 힘들다면 빌드된 샘플이 있으니 이를 실행해 볼 수도 있습니다.
(https://github.com/scouter-project/scouter-pulse/releases/tag/v0.0.1.biz-pulse-sample)

java -jar pulse.sample.bizcounter-0.0.1-SNAPSHOT.jar

 

실행하면 client에서는 이렇게 보여지게 됩니다.

%e1%84%89%e1%85%b3%e1%84%8f%e1%85%b3%e1%84%85%e1%85%b5%e1%86%ab%e1%84%89%e1%85%a3%e1%86%ba-2016-09-07-%e1%84%8b%e1%85%a9%e1%84%92%e1%85%ae-11-16-12

host 명에 실제 host 정보를 주는대신 onair-info 와 product-info 로 주어 그룹핑을 하게 하였습니다. 그리고 화면에서 object type의 이름은 Biz Sample1 임을 확인할 수 있습니다.
또한 prod-1001, 1021 등등은 agent 이름입니다.
(tomcat 인 경우 tomcat1, tomcat2 처럼 나오죠?)
agent이름에 상품코드(혹은 상품명)를 할당하였고 우측 그래프에서 처럼 각 상품별 분당 주문량을 볼 수 있습니다.

PULSE를 만들고 어떤 정보를 보여주면 재미있을까 하다가 주문수량을 보여주는 것을 만들어 보았습니다. 실제로 제가 홈쇼핑 업무를 직접 해본적은 없기에 이런 정보가 의미가 있는지는 모르겠지만 PULSE로 할수 있는 것이 어떤 것인지를 보여주는 샘플로서는 적절했던 것 같습니다.

결국 PULSE는 http를 사용하는 간단한 agent 개발 방식입니다.
HTTP를 통해 agent 타입을 등록하고, 성능 정보만 발송하면 client에서 알아서 보여주게 됩니다. 다양한 언어로 쉽게 개발할 수 있는 장점은 있지만 그만큼 수집서버에 오버헤드가 발생하니 적절히 사용되어야 할 것입니다.

그리고 PULSE와 유사하나 오버헤드가 적은 전용 통신 프로토콜을 사용하는 agent 개발 방식을 구상중에 있습니다. 일단은 java만 지원할 예정입니다.
(예정이긴 한데 언제 될지는 … ㅎㅎㅎ)

다양한 agent가 공유(contribution)되었으면 하는 바램입니다.

#고액의상품이걸린scouter-agent경진대회라도…

 

Scouter-Pulse를 이용하여 나만의 agent 만들기”에 대한 5개의 생각

  1. 이명훈댓글:

    server/conf/scouter.conf 에 net_http_server_enabled=true를 해도 6180포트가 활성화 되지 않는데 어떤걸 또 확인해봐야 될가요?

    좋아요

  2. KBS댓글:

    scouter-pulse를 어떻게 적용해야 하나요?
    net_http_server_enabled=true 적용 후 6180포트는 열렸는데요

    http://localhost:6180/register 하면 아래와 같이 나옵니다.

    HTTP ERROR: 405
    Problem accessing /register. Reason:
    HTTP method GET is not supported by this URL
    Powered by Jetty:// 8.1.19.v20160209

    Scouter version 1.7.1 입니다.

    좋아요

댓글 남기기