스프링 부트(핵심 원리와 활용)

Ch09. 모니터링 메트릭 활용 - 메트릭 등록(Timer)

webmaster 2023. 5. 9. 15:49
728x90

Timer

Timer는 좀 특별한 메트릭 측정 도구인데, 시간을 측정하는 데 사용된다.
카운터와 유사한데, Timer를 사용하면 실행 시간도 함께 측정할 수 있다.
Timer는
다음과 같은 내용을 한 번에 측정해 준다.

  • seconds_count : 누적 실행 수 - 카운터
  • seconds_sum : 실행 시간의 합 - sum
  • seconds_max : 최대 실행 시간(가장 오래걸린 실행 시간) - 게이지
    • 내부에 타임 윈도우라는 개념이 있어서 1~3분 마다 최대 실행 시간이 다시 계산된다.

OrderServiceV3

@Slf4j
public class OrderServiceV3 implements OrderService {

  private final MeterRegistry registry;
  private AtomicInteger stock = new AtomicInteger(100);

  public OrderServiceV3(MeterRegistry registry) {
    this.registry = registry;
  }

  @Override
  public void order() {
    Timer timer = Timer.builder("my.order")
        .tag("class", this.getClass().getName())
        .tag("method", "order")
        .description("order")
        .register(registry);
    timer.record(() -> {
      log.info("주문");
      stock.decrementAndGet();
      sleep(500);
    });
  }


  @Override
  public void cancel() {
    Timer timer = Timer.builder("my.order")
        .tag("class", this.getClass().getName())
        .tag("method", "cancel")
        .description("cancel")
        .register(registry);
    timer.record(() -> {
      log.info("취소");
      stock.incrementAndGet();
      sleep(200);
    });
  }

  @Override
  public AtomicInteger getStock() {
    return stock;
  }

  private void sleep(int l) {
    try {
      Thread.sleep(l + new Random().nextInt(200));
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
  }
}
  • Timer.builder(name) 를 통해서 타이머를 생성한다. name 에는 메트릭 이름을 지정한다.
  • tag 를 사용했는데, 프로메테우스에서 필터할 수 있는 레이블로 사용된다.
  • 주문과 취소는 메트릭 이름은 같고 tag 를 통해서 구분하도록 했다.
  • register(registry) : 만든 타이머를 MeterRegistry 에 등록한다. 이렇게 등록해야 실제 동작한다.
  • 타이머를 사용할 때는 timer.record() 를 사용하면 된다. 그 안에 시간을 측정할 내용을 함수로 포함하면 된다.
  • 걸리는 시간을 확인하기 위해 주문은 0.5, 취소는 0.2초 대기하도록 했다. 추가로 가장 오래 걸린 시간을 확인하기 위해 sleep() 에서 최대 0.2초를 랜덤하게 더 추가했다. (모두 0.5초로 같으면 가장 오래 걸린 시간을 확인하기 어렵다.)

OrderConfigV3

@Configuration
public class OrderConfigV3 {

  @Bean
  public OrderService orderService(MeterRegistry registry) {
    return new OrderServiceV3(registry);
  }
}

ActuatorApplication - 변경

@Import(OrderConfigV3.class)
@SpringBootApplication(scanBasePackages = "hello.controller")
public class ActuatorApplication {

  public static void main(String[] args) {
    SpringApplication.run(ActuatorApplication.class, args);
  }

  @Bean
  public InMemoryHttpExchangeRepository httpExchangeRepository() {
    return new InMemoryHttpExchangeRepository();
  }
}
  • OrderConfigV2 -> OrderConfigV3 로 변경한다.

실행

  • http://localhost:8080/order: order 접속
  • http://localhost:8080/cancel: cancel 접속
  • http://localhost:8080/actuator/metrics/my.order: 액츄에이터 메트릭 확인
    • 타이머를 사용하면 총 3가지 측정 항목이 생기는 것을 확인할 수 있다.
      • measurements 항목을 보면 COUNT , TOTAL_TIME , MAX 이렇게 총 3가지 측정 항목을 확인할 수 있다. COUNT : 누적 실행 수(카운터와 같다)
      • TOTAL_TIME : 실행 시간의 합(각각의 실행 시간의 누적 합이다)
      • MAX : 최대 실행 시간(가장 오래 걸린 실행시간이다)
  • http://localhost:8080/actuator/prometheus : 프로메테우스 포맷 메트릭 확인
    • 프로메테우스로 다음 접두사가 붙으면서 3가지 메트릭을 제공한다.
      • seconds_count : 누적 실행 수
      • seconds_sum : 실행 시간의 합
      • seconds_max : 최대 실행 시간(가장 오래 걸린 실행 시간), 프로메테우스 gague
    • 참고: 내부에 타임 윈도우라는 개념이 있어서 1~3분 마다 최대 실행 시간이 다시 계산된다
    • 여기서 평균 실행 시간도 계산할 수 있다.
      • seconds_sum / seconds_count = 평균 실행시간

그라파나 등록 - 주문수 v3

패널 옵션

  • Title : 주문수 v3

PromQL

  • increase(my_order_seconds_count{method="order"}[1m])
    • Legend : {{method}}
  • increase(my_order_seconds_count{method="cancel"}[1m])
    • Legend : {{method}}

참고: 카운터는 계속 증가하기 때문에 특정 시간에 얼마나 증가했는지 확인하려면 increase() , rate() 같은 함수와 함께 사용하는 것이 좋다.

그라파나 등록 - 최대 실행시간

패널 옵션

  • Title :최대 실행시간

PromQL

  • my_order_seconds_max

그라파나 등록 - 평균 실행시간

패널 옵션

  • Title : 평균 실행시간

PromQL

  • increase(my_order_seconds_sum[1m]) / increase(my_order_seconds_count[1m])
728x90