1. Micrometer 소개
- Micrometer는 JVM 기반 애플리케이션을 위한 메트릭 계측 라이브러리입니다.
- 가장 인기 있는 모니터링 시스템들의 계측 클라이언트들에 대한 간단한 파사드(facade)를 제공하여, 벤더 종속 없이 JVM 기반 애플리케이션 코드에 계측을 추가할 수 있게 해줍니다.
- 메트릭 수집 작업에 최소한의 오버헤드만 추가하면서 메트릭의 이식성을 최대화하도록 설계되었습니다.
1.1 의존성
micrometer-core
모듈은 최소한의 의존성만을 갖습니다.- 일시 중지 감지(pause detection) 기능 사용 시 LatencyUtils 의존성이 필요합니다.
- 클라이언트 측 백분위수 사용 시 HdrHistogram이 필요합니다.
2. 주요 특징
2.1 일반적 특징
- 벤더 중립적인 메트릭 파사드
- 다양한 모니터링 시스템 지원 (Prometheus, Graphite, DataDog 등)
- 차원 기반의 메트릭 수집
- 풍부한 메트릭 타입 제공
2.2 지원하는 모니터링 시스템
- Micrometer는 크게 세 부분으로 구성됩니다.
- 계측 SPI가 포함된 코어 모듈(모든 모니터링 시스템에 공통적인 인터페이스)
- 다양한 모니터링 시스템용 구현체가 포함된 모듈들(각각을 레지스트리라고 함)
- 테스트 킷
- 이러한 구조 덕분에 이러한 구조 덕분에 개발자는 코어 모듈의 API만 사용하면 되고, 실제로 어떤 모니터링 시스템을 사용할지는 나중에 선택할 수 있습니다.
SPI(Service Provider Interface)
SPI(Service Provider Interface)는 서비스 제공자가 구현하고자 하는 인터페이스를 정의한 API의 집합입니다. 쉽게 말하면, 특정 서비스를 구현할 때 지켜야 하는 규약이나 약속을 의미합니다. Micrometer의 경우를 예로 들면:
Micrometer는 메트릭을 수집하는 인터페이스(SPI)를 정의합니다. 각 모니터링 시스템(Prometheus, Graphite 등)은 이 인터페이스를 구현합니다. 덕분에 개발자는 하나의 API만 사용하면 되고, 여러 모니터링 시스템을 쉽게 바꿔가며 사용할 수 있습니다.
차원성(Dimensionality)
메트릭 이름에 태그 키/값 쌍을 추가할 수 있는지에 대한 특성입니다.
차원적 시스템:
- 태그 키/값 쌍으로 메트릭을 풍부하게 만들 수 있음
- 지원 시스템: AppOptics, Atlas, Azure Monitor, Cloudwatch, Datadog, Datadog StatsD, Dynatrace, Elastic, Humio, Influx, KairosDB, New Relic, Prometheus, SignalFx, Sysdig StatsD, Telegraf StatsD, Wavefront
계층적 시스템:
- 단순한 평면 메트릭 이름만 지원
- Micrometer는 태그 키/값 쌍을 평면화하여 이름에 추가
- 지원 시스템: Graphite, Ganglia, JMX, Etsy StatsD
비율 집계(Rate Aggregation)
지정된 시간 간격 동안의 샘플 집합 집계 방식에 대한 특성입니다.
클라이언트 측 집계:
- 애플리케이션에서 이산 샘플(예: 카운트)을 비율로 변환하여 전송
- 지원 시스템: AppOptics, Atlas, Azure Monitor, Datadog, Dynatrace, Elastic, Graphite, Ganglia, Humio, Influx, JMX, Kairos, New Relic, 모든 StatsD 변종들, SignalFx
서버 측 집계:
- 누적 값을 서버에서 집계
- 지원 시스템: Prometheus, Wavefront
게시(Publishing)
메트릭 데이터 전송 방식에 대한 특성입니다.
클라이언트 푸시 방식:
- 애플리케이션이 정기적인 간격으로 메트릭을 시스템에 푸시
- 지원 시스템: AppOptics, Atlas, Azure Monitor, Datadog, Dynatrace, Elastic, Graphite, Ganglia, Humio, Influx, JMX, Kairos, New Relic, SignalFx, Wavefront
서버 폴링 방식:
- 모니터링 시스템이 애플리케이션으로부터 메트릭을 폴링
- 지원 시스템: Prometheus, 모든 StatsD 변종들
추가 고려사항
- 모니터링 시스템마다 다음과 같은 세부적인 차이가 있습니다:
- 측정의 기본 단위(특히 시간)에 대한 개념
- 메트릭의 표준 명명 규칙
- 기타 시스템별 특수 요구사항
- Micrometer는 이러한 각 시스템의 요구사항에 맞춰 레지스트리별로 메트릭을 자동으로 커스터마이즈합니다.
3. Meter와 Registry
3.1 Meter
- Meter는 Micrometer의 핵심 인터페이스로, 메트릭 수집을 위한 기본 단위입니다.
- 다양한 Meter 타입이 존재하며, 각각의 용도가 다릅니다:
- Gauge: 현재 값을 측정 (예: 현재 활성 사용자 수)
- Timer: 시간 관련 측정 (예: API 응답 시간)
- Counter: 증가하는 값 측정 (예: 총 요청 수)
- 메트릭 수집은 성능에 큰 영향을 미치지 않도록 설계되어 있습니다.
- 각 Meter는 이름과 태그(차원)로 구분되며, 이를 통해 데이터를 다양한 관점에서 분석할 수 있습니다.
3.2 Registry
- Micrometer의 Meter들은 MeterRegistry에서 생성되고 관리됩니다.
- 지원되는 각 모니터링 시스템마다 MeterRegistry의 구현체가 있습니다.
- 레지스트리를 생성하는 방법은 각 구현체마다 다릅니다.
SimpleMeterRegistry
- Micrometer는 각 meter의 최신 값을 메모리에 보관하고 데이터를 어디에도 내보내지 않는 SimpleMeterRegistry를 포함합니다.
- 아직 선호하는 모니터링 시스템이 없다면, simple 레지스트리를 사용하여 메트릭 작업을 시작할 수 있습니다
Composite Registries
- Micrometer는 여러 레지스트리를 추가할 수 있는 CompositeMeterRegistry를 제공하여 여러 모니터링 시스템에 동시에 메트릭을 게시할 수 있게 합니다
4. Counter
- 가장 단순한 메트릭으로, 단순히 숫자를 세는 용도입니다
- 오직 증가만 가능하며, 감소는 불가능합니다
- 예: API 호출 횟수, 에러 발생 횟수 등
- Timer나 DistributionSummary로 측정할 수 있는 것은 카운터로 측정하지 않는 것이 좋습니다
- Timer와 DistributionSummary는 이미 자체적으로 count를 포함하고 있습니다
- 절대값보다는 "시간당 발생 비율"에 집중하세요
- 예: "총 1000건의 에러" 보다는 "분당 10건의 에러 발생" 이 더 유용합니다
5. Gauge
- 현재 값을 보여주는 측정기입니다
- 시계의 온도계처럼 현재 상태를 보여줍니다
- 증가/감소가 모두 가능합니다
- 주로 사용하는 예:
- 컬렉션이나 맵의 현재 크기
- 현재 실행 중인 스레드 수
- 메모리 사용량
- CPU 사용률
- 게이지 사용 시 주의사항
- 자연스러운 상한선이 있는 것을 모니터링할 때 사용하세요
- 계속 증가하는 값(예: 요청 수)은 게이지로 측정하지 마세요
- Counter로 측정할 수 있는 것은 게이지로 측정하지 마세요
6. Timer
- 짧은 시간 지연(latency)과 이벤트 빈도를 측정하기 위한 도구
- 모든 Timer는 최소한 두 가지를 측정:
- 총 소요 시간
- 이벤트 발생 횟수
- 백엔드 시스템에 따라 추가로 측정:
- 최대값
- 백분위수
- 히스토그램
- Timer의 주요 특징
- 음수 값은 지원하지 않음
- 너무 긴 시간 측정은 피해야 함 (최대 292.3년)
- 시간 단위는 모니터링 시스템에 따라 자동 변환됨
6.1 Timer 사용 방법
// 기본적인 Timer 생성
Timer timer = Timer
.builder("my.timer")
.description("설명")
.tags("region", "test")
.register(registry);
// 시간 측정 방법들
timer.record(() -> doSomething()); // 반환값 필요없는 경우
timer.recordCallable(() -> getValue()); // 반환값 필요한 경우
// Timer.Sample 사용
Timer.Sample sample = Timer.start(registry);
Response response = doSomething();
sample.stop(registry.timer("my.timer",
"status", response.status()));
6.2 @Timed 어노테이션 사용
@Configuration
public class TimedConfig {
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
@Service
public class ExampleService {
@Timed
public void someMethod() {
// 이 메서드의 실행 시간이 자동으로 측정됨
}
@Timed
@Async
public CompletableFuture<?> asyncMethod() {
// CompletableFuture가 완료될 때까지의 시간 측정
return CompletableFuture.supplyAsync(() -> ...);
}
}
- 메서드의 실행 시간을 자동으로 측정하려면
@Timed
어노테이션을 사용하세요
@Timed
public void processOrder(@MeterTag("orderId") String id) {
// orderId 태그가 자동으로 추가됨
}
@MeterTag
어노테이션을 사용하여 메서드 파라미터를 태그로 추가할 수 있습니다