-
Micrometer Meter 관련 성능 이슈develop 2023. 10. 28. 22:56
Micrometer를 통한 metrics을 관리하고 있는데, Profiling 시 가장 많은 CPU를 소모하고 있어서 분석을 해 보았습니다.
meterRegistry.find("testMetricName").timer();
문제되는 코드인데, meterRegistry에서 단순 조회를 한다고 생각했는데 MeterRegistry.find()의 내부 구현은 다음과 같습니다.
Class MicrometerRegistry { public Search find(String name) { return Search.in(this).name(name); } } class Search { public Timer timer() { return findOne(Timer.class); } private <M extends Meter> M findOne(Class<M> clazz) { return meterStream().filter(clazz::isInstance).findAny().map(clazz::cast).orElse(null); } private Stream<Meter> meterStream() { Stream<Meter> meterStream = registry.getMeters().stream().filter(m -> nameMatches.test(m.getId().getName())); if (!tags.isEmpty() || !requiredTagKeys.isEmpty() || !tagMatches.isEmpty()) { meterStream = meterStream.filter(m -> isTagsMatched(m.getId())); } return meterStream; } }
여기서부터 좀 고민됩니다. find()에서 Search.in()으로 Search 객체를 생성하는데, Timer를 얻어 올 때에 findOnce()의 구현 이 stream을 생성해서 iteration 하게 됩니다.
MicrometerRegistry의 구현은 다음과 같습니다.
class MeterRegistry { private final Map<Id, Meter> meterMap = new ConcurrentHashMap<>(); public List<Meter> getMeters() { return Collections.unmodifiableList(new ArrayList<>(meterMap.values())); } /** * Iterate over each meter in the registry. * @param consumer Consumer of each meter during iteration. */ public void forEachMeter(Consumer<? super Meter> consumer) { meterMap.values().forEach(consumer); } }
객체 생성 관점에서 Search -> meterMap.values() -> ArrayList -> unmodifiableList -> stream 의 주요 객체가 생성되고 있어서 메모리 overhead가 상당하네요.
이 부분은 다음과 같이 수정했습니다. 일반적으로 사용할 수는 없고 제 서비스 특화된 구현이기 때문에 어디까지나 참고사항입니다.
Timer findTimer(List<Tag> tags) { AtomicReference<Timer> timer = new AtomicReference<>(null); meterRegistry.forEachMeter(it -> { if ("testMetricsName".equals(it.getId().getName()) && it instanceof Timer && isTagsMatched(it.getId(), tags)) { timer.set((Timer) it); } }); return timer.get(); } private boolean isTagsMatched(Meter.Id id, List<Tag> tags) { HashSet<Tag> set = new HashSet<>(); id.getTagsAsIterable() .forEach(set::add); return set.containsAll(tags); }
P.S. 위의 findTimer()에는 버그가 있습니다.
'develop' 카테고리의 다른 글
Cloud Native Spring Log4j Logging (0) 2024.03.03 REST API design guide (1) 2024.02.12 Kubernetes에서 node 내 thread 분리 이슈 (0) 2023.08.20 Ref: Sync 10,000 Argo CD Applications in One Shot (0) 2023.06.29 Spring Cloud Config (0) 2023.05.01