일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Design Patterns
- elasticsearch
- MySQL
- 요리
- redis
- Git
- db
- Spring Batch
- jsp
- it
- javascript
- 맛집
- springboot
- JVM
- java
- AWS
- php
- devops
- Spring
- ubuntu
- ReactJS
- Oracle
- Gradle
- Web Server
- laravel
- tool
- jenkins
- Spring Boot
- linux
- IntelliJ
Archives
- Today
- Total
아무거나
[Spring Actuator] Aspect 를 활용한 특정 metric 의 tag 의 여러 value 값 별로 시간단위로 실시간 카운트 저장 본문
Java & Kotlin/Spring
[Spring Actuator] Aspect 를 활용한 특정 metric 의 tag 의 여러 value 값 별로 시간단위로 실시간 카운트 저장
전봉근 2023. 12. 4. 18:49반응형
Aspect 를 활용한 특정 metric 의 tag 의 여러 value 값 별로 시간단위로 실시간 카운트 저장
예시로 호출시 특정 타입번호기준으로 페이지를 이동하는 기능에 타입번호_시간단위로 카운트를 저장하는 기능을 생성 (참고: https://github.com/bkjeon1614/java-example-code/tree/develop/bkjeon-mybatis-codebase)
- Custom Annotation 생성
[CustomCounter.java]
package com.example.bkjeon.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomCounter {
}
- Counter Metric 에 지정할 Metric Name, Tag Name 관련 Enum 생성
[MetricType.java]
package com.example.bkjeon.enums.actuator;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Metric 관련 Enum
* metricName: 메트릭명
* tagName: 호출된 메소드명
*/
@Getter
@AllArgsConstructor
public enum MetricType {
CUSTOM_METRIC_INFO("custom.metric.count", "isCustomCounterReq");
private final String metricName;
private final String tagName;
}
- Aspect 생성
[CounterAspect.java]
package com.example.bkjeon.base.aspect;
import com.example.bkjeon.base.services.api.v1.actuator.service.counter.CustomCounterService;
import com.example.bkjeon.enums.actuator.MetricType;
import javax.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.joda.time.LocalDateTime;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 실시간 카운트 집계
*/
@Slf4j
@Component
@Aspect
@RequiredArgsConstructor
public class CounterAspect {
private final CustomCounterService customCounterService;
@Pointcut("@annotation(com.example.bkjeon.annotation.CustomCounter)")
private void isCustomCounter(){}
@Before("isCustomCounter()")
public void isCustomIncrCount() {
try {
LocalDateTime nowDateTime = new LocalDateTime();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes()).getRequest();
String valueName = request.getServletPath().split("/")[4]
+ "_"
+ nowDateTime.toString("yyMMddHHmmss");
customCounterService.increment(
MetricType.CUSTOM_METRIC_INFO.getMetricName(),
MetricType.CUSTOM_METRIC_INFO.getTagName(),
valueName,
2,
10
);
log.debug("Value Size: {}", customCounterService.getSize(
MetricType.CUSTOM_METRIC_INFO.getMetricName(),
MetricType.CUSTOM_METRIC_INFO.getTagName(), valueName));
log.debug("Value Sum Size: {}", customCounterService.getAllValueSumSize(
MetricType.CUSTOM_METRIC_INFO.getMetricName(),
MetricType.CUSTOM_METRIC_INFO.getTagName(), valueName, 2));
} catch (Throwable throwable) {
log.error("isDshopSetCount(Aspect) ERROR !! {}", (Object) throwable.getStackTrace());
}
}
}
- Counter Service 생성
[CustomCounterService.java]
package com.example.bkjeon.base.services.api.v1.actuator.service.counter;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CustomCounterService {
private final MeterRegistry meterRegistry;
/**
* 카운트 저장
* @param metricName metric 명
* @param tagName tag 명
* @param valueName tag value 값 저장 (Ex: {매장/기획전 번호}_{yyMMddHHmmss})
* @param delLimit 최근 시간 오름 차순 으로 이전에 먼저 등록된 데이터 를 제거할 값의 개수
*/
public void increment(String metricName, String tagName, String valueName, int delLimit,
int maintainLimit) {
List<Meter> meterList = meterRegistry.getMeters().stream()
.filter(meter -> metricName.equals(meter.getId().getName()))
.collect(Collectors.toList());
List<Tag> tagList = meterList.stream()
.flatMap(meter -> meter.getId().getTags().stream())
.filter(tag -> valueName.split("_")[0].equals(tag.getValue().split("_")[0]))
.sorted(Comparator.comparing(Tag::getValue))
.collect(Collectors.toList());
List<Tag> removeTagList = tagList.stream()
.limit(delLimit)
.collect(Collectors.toList());
if (tagList.size() > maintainLimit) {
for (Tag tag: removeTagList) {
remove(metricName, tag.getKey(), tag.getValue());
}
}
Counter.builder(metricName)
.tag(tagName, valueName)
.register(meterRegistry).increment();
}
/**
* 특정 tag value 의 카운트 조회
* @param metricName metric 명
* @param tagName tag 명
* @param valueName tag value 값 저장 (Ex: {매장/기획전 번호}_{yyMMddHHmmss})
*/
public Double getSize(String metricName, String tagName, String valueName) {
return Counter.builder(metricName)
.tag(tagName, valueName)
.register(meterRegistry).count();
}
/**
* 특정 tag value 의 최근 시간 내림 차순 으로 총 n 개의 카운트 값 을 sum 하여 가져옴
* @param metricName metric 명
* @param tagName tag 명
* @param valueName tag value 값 저장 (Ex: {매장/기획전 번호}_{yyMMddHHmmss})
* @param limit 카운트 를 집계할 최근 시간 내림 차순 으로 sum 할 개수
*/
public Double getAllValueSumSize(String metricName, String tagName, String valueName, int limit) {
List<Meter> meterList = meterRegistry.getMeters().stream()
.filter(meter -> metricName.equals(meter.getId().getName()))
.collect(Collectors.toList());
return meterList.stream()
.flatMap(meter -> meter.getId().getTags().stream())
.filter(tag -> valueName.split("_")[0].equals(tag.getValue().split("_")[0]))
.sorted(Comparator.comparing(Tag::getValue).reversed())
.limit(limit)
.mapToDouble(tag -> Counter.builder(metricName).tag(tagName, tag.getValue())
.register(meterRegistry).count())
.sum();
}
/**
* 특정 tag value 를 삭제
* @param metricName metric 명
* @param tagName tag 명
* @param valueName tag value 값 저장 (Ex: {매장/기획전 번호}_{yyMMddHHmmss})
*/
private void remove(String metricName, String tagName, String valueName) {
meterRegistry.remove(Counter.builder(metricName)
.tag(tagName, valueName)
.register(meterRegistry));
}
}
- Counter Controller 기능 추가
[CounterController.java]
package com.example.bkjeon.base.services.api.v1.actuator.controller;
import com.example.bkjeon.annotation.CustomCounter;
import com.example.bkjeon.base.services.api.v1.actuator.service.ApplicationRequestManager;
import com.example.bkjeon.base.services.api.v1.actuator.service.ApplicationRequestWithoutMicrometer;
import io.micrometer.core.annotation.Counted;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("v1/counter")
@RequiredArgsConstructor
public class CounterController {
...
/**
* 실시간 카운트 집계
*/
@CustomCounter
@GetMapping("customCounterReq/{typeNo}")
public String isCustomCounterReq(@PathVariable Long typeNo) {
return HttpStatus.OK.getReasonPhrase();
}
}
반응형
'Java & Kotlin > Spring' 카테고리의 다른 글
@Async 를 활용한 비동기 호출 구현 (0) | 2024.10.28 |
---|---|
[Spring Actuator] Example 6편 - Timer (1) | 2023.11.30 |
[Spring Actuator] Example 5편 - Gauge (2) | 2023.11.27 |
[Spring Actuator] Example 4편 - Metrics Tags (0) | 2023.11.27 |
[Spring Actuator] Example 3편 - Counter (0) | 2023.11.26 |
Comments