마이크로미터: Undertow 요청 메트릭 등록

xellos·2023년 4월 13일
0

성능관리

목록 보기
4/4

들어가기전...

  • 필자의 회사에서는 undertow 를 WAS 로 사용하고 있다.
  • micrometer는 tomcat 과 netty 서버의 지표는 자동으로 수집해서 볼 수 있으나, undertow 의 지표는 수집하지 않았다.
  • 따라서 undertow 의 지표를 별도로 수집하기 위해 micrometer 가 이를 수집해서 등록할 수 있도록 설정해한다.

요청

여기서는 요청과 관련된 부분을 등록하는 것을 다룬다.

1) Request.java

  • undertow 가 제공하는 Handler 정보를 가져와 micrometer 에 등록시 미리 사용할 수 있도록 name 과 desc 정보가 담긴 enum 클래스를 생성한다.
@Getter
@AllArgsConstructor
public enum Request {
	REQUESTS("undertow.requests", "Number of requests"),
  	REQUEST_TIME_MAX("undertow.request.time.max", "The longest request duration in time"),
  	REQUEST_TIME_MIN("undertow.request.time.min", "The shortest request duration in time"),
  	REQUEST_ERRORS("undertow.request.errors", "Total number of error requests");
    
    private String name;
    private String desc;
}

2) UndertowMetricsHandlerWrapper.java

  • Undertow 가 제공하는 HandlerWrapper 를 구현하는 클래스를 생성한다.
  • 이때, Undertow 가 제공하는 MetricHandler 를 생성해서 반환한다.
  • 추수 HandlerList 에 등록해서 사용한다.
@Getter
@Component
public class UndertowMetricsHandlerWrapper implements HandlerWrapper {

	private MetricHandler metricsHandler;
    
    @Override
    public HttpHandler wrap(HttpHandler httpHandler) {
    	metricsHandler = new MetricHandler(httpHandler);
        return metricsHandler;
    }
}

io.undertow.server.handlers.MetricHandler 는 내부에 MetricResult 클래스를 가지고 있는데, 여기에 접근해서 요청과 관련된 정보를 micrometer 가 사용할 수 있도록 등록할 수 있다.

public static class MetricResult {
    private volatile long totalRequestTime;
    private volatile int maxRequestTime;
    private volatile int minRequestTime = -1;
    private volatile long totalRequests;
    private volatile long totalErrors;

    public long getTotalRequestTime() { return this.totalRequestTime; }
    public int getMaxRequestTime() { return this.maxRequestTime; }
    public int getMinRequestTime() { return this.minRequestTime; }
    public long getTotalRequests() { return this.totalRequests; }
    public long getTotalErrors() { return this.totalErrors; }
    
    ...
}

3) UndertowConfig.java

  • 위에서 만든 Wrapper 를 HandlerChain 에 등록한다.
@Configuration
public class UndertowConfig {
	
    @Bean
    public UndertowDeploymentInfoCustomizer undertowDeploymentInfoCustomizer(
    	UndertowMetricsHandlerWrapper undertowMetricsHandlerWrapper) {
        
    return deploymentInfo -> deploymentInfo
    							.addOuterHandlerChainWrapper(undertowMetricsHandlerWrapper);
}

4) UndertowMeterBinder.java

  • 등록한 UndertowMetricHandlerWrapper 에 micrometer 지표를 등록한다.
@Slf4j
@Component
@RequiredArgsConstructor
public class UndertowMeterBinder implements ApplicationListener<ApplicationReadyEvent> {

  private final UndertowMetricsHandlerWrapper undertowMetricsHandlerWrapper;
  
  @Override
  public void onApplicationEvent(ApplicationReadyEvent aplicationReadyEvent) {
    bindTo(applicationReadyEvent.getApplicationContext().getBean(MeterRegistry.class));
  }
  
  public void bindTo(MeterRegistry meterRegistry) {
    bind(meterRegistry, undertowMetricshandlerWrapper.getMetricsHandler());
  }
  
  public void bind(MeterRegistry registry, MetricsHandler metricsHandler) {
    //request 관련 metric 지표등록
    bindTimer(registry, REQUEST.getName(), REQUESTS.getDesc(), metricsHandler,
      m -> m.getMetrics().getTotalRequests(), m2 -> m2.getMetrics().getMinRequestTime());
      
    bindTimeGauge(registry, REQUEST_TIME_MAX.getName(), REQUEST_TIME_MAX.getDesc(), metricsHandler,
      m -> m.getMetrics().getMaxRequestTime());
      
    bindTimeGauge(registry, REQUEST_TIME_MIN.getName(), REQUEST_TIME_MIN.getDesc(), metricsHandler,
      m -> .getMetrics().getMinRequestTime());
      
    bindCounter(registry, REQUEST_ERRORS.getName(), REQUEST_ERRORS.getDesc(), metricshandler,
      m -> m.getMetrics().getTotalErrors());
  }
  
  private void bineTimer(
    MeterRegistry registry, String name, String desc, MetricsHandler metricsHandler,
    ToLongFunction<MetricsHandler> countFunc, ToDoubleFunction<Metricshadnler> consumer
  ) {
    FunctionTimer.builder(name, metricsHandler, countFunc, consumer, TimeUnit.MILLISECONDS)
      .description(desc)
      .register(registry);
  }
  
  private void bindTimeGauge(
    MeterRegistry registry, String name, String desc, MetricsHandler metricResult,
    ToDoubleFunction<MetricsHandler> consucmer
  ) {
    TimeGauge.builder(name, metricResult, TimeUnit.MILLISECONDS, consumer)
      .description(desc)
      .register(registry);
  }
  
  private void bindCounter(
    MeterRegistry registry, String name, String desc, MetricsHandler metricsHandler,
    ToDoubleFunction<MetricsHandler> consumer
  ) {
    FunctionCounter.builder(name, metricsHandler, consumer)
      .description(desc)
      .register(registry);
  }
}

0개의 댓글