이전 포스팅에서 커스텀 메트릭 등록을 위한 간단한 예제프로그램을 만들어 보았다.
이번 포스팅에서는 예제 프로그램을 활용하여 커스텀 메트릭을 만들어 보겠다.
1) 카운터 ( Counter )
시간에 따라 '증가'만하는 데이터이다. 주문수는 항상 증가한다. 중간에 감소하지 않는다.
2) 게이지 ( Gauage )
시간에 따라 '증가'와 '감소'를 반복하는 데이터이다. 재고량은 쌓였다가 줄었다가 반복한다.
3) 타이머 ( Timer )
시간과 관련된 데이터이다. 실행횟수,실행시간의 합, 최장실행시간 등 시간과 관련된 정보를 제공한다.
카운터(Counter)
MeterResistry는 데이터를 메트릭으로 등록하는 마이크로미터 모듈이다. MeterResistry는 스프링이 자동으로 IOC컨테이너에 Bean으로 등록하므로 의존관계 주입만 하면 사용할 수 있다.
OrderConfig 클래스
@Configuration
public class OrderConfig {
@Bean // MeterRegistry Bean 객체 주입
public OrderService orderService(MeterRegistry meterRegistry){
return new OrderServiceV0(meterRegistry);
}
}
설정클래스에서 Bean등록할 때 MeterRegistry 객체를 주입해준다.
OrderServiceV0 클래스
package com.example.demo.orderservice;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@RequiredArgsConstructor
public class OrderServiceV0 implements OrderService{
private final MeterRegistry meterRegistry;
private AtomicInteger stock = new AtomicInteger(100);
@Override
public void order() {
log.info("주문...");
stock.decrementAndGet();
//카운터 등록 ( 주문수 )
Counter.builder("my.order") // 이름
.tag("class",this.getClass().getName()) // 태그1 ( class )
.tag("method","order") // 태그2 (method)
.description("order") // 설명
.register(meterRegistry).increment(); // register : 등록, increment()_: 메트릭 measurments 속성의 value + 1
}
@Override
public void cancel() {
log.info("주문취소...");
stock.incrementAndGet();
//카운터 등록 ( 취소수 )
Counter.builder("my.order") // 이름
.tag("class",this.getClass().getName()) // 태그1 ( class )
.tag("method","cancel") // 태그2 ( method )
.description("order") // 설명
.register(meterRegistry).increment(); // register : 등록, increment()_: 메트릭 measurments 속성의 value + 1
}
@Override
public AtomicInteger getStock() {
return stock;
}
}
두 클래스를 제외하고는 나머지 코드는 이전과 동일하다.
그럼 실행시켜보겠다.
▹ 주문수행 : http://localhost:8080/order
▹ 취소수행 : http://localhost:8080/cancel
주문과 실행을 수행한 후, 메트릭값이 잘 나오는지 확인해보자.
▹ 메트릭 정보 확인 : http://localhost:8080/actuator/metrics/my.order
JSON 방식으로 메트릭이 생성되었음을 확인할 수 있다. 나는 주문과 취소를 총 3회 수행해서 호출횟수의 VALUE가 3이다. availableTags 는 해당 메트릭에 등록된 태그를 보여준다. 우리는 method 태그와 class 태그를 추가해서 위와 같다.
AOP로 코드 리팩토링하기
Counter로 커스텀 메트릭을 생성하는 과정의 코드가 반복된다. 이는 AOP를 사용하면 코드를 줄여줄 수 있다.
이전 코드는 개발자가 직접 MeterRegistry를 조작했다. 이번에는 MeterRegistry를 주입받은 CountedAspect에게 메트릭 생성을 일임할 것이다. CountedAspect 객체가 Bean으로 등록하면 @Counted 어노테이션이 달린 메소드들을 읽고 메트릭으로 자동등록한다. 이렇게 되면 개발자가 굳이 메트릭 등록 코드를 짤 필요가 사라진다.
build.gradle에 AOP 의존관계를 하나 추가한다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
OrderConfig 클래스
@Configuration
public class OrderConfig {
@Bean // CountedAspect Bean으로 등록하기 ( MeterRegistry 주입 )
public CountedAspect countedAspect(MeterRegistry meterRegistry){
return new CountedAspect(meterRegistry);
}
@Bean // OrderService 버전업 Bean등록
public OrderService orderService(){
return new OrderServiceV1();
}
}
CountedAspect 객체를 Bean으로 등록하였다. 그리고 OrderService는 리팩토링 된 OrderServiceV1으로 버전 업그레이드 하였다.
OrderServiceV1 클래스
@Slf4j
public class OrderServiceV1 implements OrderService{
private AtomicInteger stock = new AtomicInteger(100);
@Override
@Counted("my.order") // 어노테이션 하나로 메트릭 생성
public void order() {
log.info("주문...");
stock.decrementAndGet();
}
@Override
@Counted("my.order") // 어노테이션 하나로 메트릭 생성
public void cancel() {
log.info("주문취소...");
stock.incrementAndGet();
}
@Override
public AtomicInteger getStock() {
return stock;
}
}
코드가 확실히 단순해졌다.
어노테이션에 이름만 정해주면 알아서 여러 태그와 정보를 넣어서 메트릭을 생성한다.
그럼 프로젝트를 재실행하여 메트릭데이터를 확인해보자.
▹ URL : http://localhost:8080/actuator/metrics/my.order
// 20230516003356
// http://localhost:8080/actuator/metrics/my.order
{
"name": "my.order",
"measurements": [
{
"statistic": "COUNT",
"value": 2.0
}
],
"availableTags": [
{
"tag": "result",
"values": [
"success"
]
},
{
"tag": "exception",
"values": [
"none"
]
},
{
"tag": "method",
"values": [
"cancel",
"order"
]
},
{
"tag": "class",
"values": [
"com.example.demo.orderservice.OrderServiceV1"
]
}
]
}
이전과 달리 result, exception 태그가 추가되었다. AOP로 리팩토링된 코드도 잘 출력됨을 확인하였다.
▹ URL : http://localhost:8080/actuator/prometheus
마이크로미터는 JSON메트릭을 프로메테우스용 메트릭 변형본도 제공한다. 프로메테우스는 해당 URL로 일정주기로 접근하여 메트릭을 수집한다. my.order는 my_order_total로 이름이 바뀌었다. 카운테 메트릭은 끝에 _total로 바뀐다.
프로메테우스, 그라파나 연동하기
프로메테우스 서버를 실행하고 SpringBoot 프로젝트와 연동하였다.
위 그림과 같이,
spring-actuator Endpoint의 status가 up이면 정상적으로 연동이 완료된 것이다.
그라파나 서버를 실행하고 Prometheus와 연동하였다.
그라파나는 프로메테우스에 쿼리를 보내 데이터를 가져온다.
▹ 주문메트릭쿼리 : increase(my_order_total{method="order"}[1m])
▹ 취소메트릭쿼리 : increase(my_order_total{method="cancel"}[1m])
그럼 대시보드를 만들고 주문과 취소를 연속으로 수행하여 주문수와 취소수의 카운트의 추이가 시각화 되는지 보자.
▹ 주문수행 : http://localhost:8080/order
▹ 취소수행 : http://localhost:8080/cancel
주문수와 취소수는 카운터로 항상 '증가'만 한다. 우리가 원하는 데이터는 시간대별 증가'추이'이다.
주문수는 증가하지만 주문추이는 감소할 수 있다.
취소수는 증가하지만 취소추이는 감소할 수 있다.
위 차트처럼 증가했다가 잠시감소했다가 다시 증가하는 이유는 increase()를 사용해서이다. increase를 사용하면 증가추이를 확인할 수 있다. 이렇게 커스텀 메트릭도 데이터가 시각화를 완료하였다. 마이크로미터가 제공하는 기본 메트릭이 아닌 커스텀 메트릭도 마이크로미터에 등록할 수 있다.
참고자료
'SPRING > Spring Boot' 카테고리의 다른 글
[SpringBoot] 커스텀 메트릭(Metric) 등록하기 -Timer (0) | 2023.05.18 |
---|---|
[SpringBoot] 커스텀 메트릭(Metric) 등록하기 - 예제 만들기 (0) | 2023.05.15 |
[SpringBoot] 메트릭(Metric) 이란? (0) | 2023.05.11 |
[SpringBoot] 엑츄에이터(Actuator)(3) - 로그 및 HTTP 기록 확인하기 (0) | 2023.05.11 |
[SpringBoot] 엑츄에이터(Actuator)(2) - info 엔드포인트 (0) | 2023.05.11 |