All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zodiac.actuate.loadbalancer.stats.StatsAppLoadBalancerLifecycle Maven / Gradle / Ivy

There is a newer version: 1.6.8
Show newest version
package org.zodiac.actuate.loadbalancer.stats;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;

import org.zodiac.core.application.AppInstance;
import org.zodiac.core.bootstrap.loadbalancer.AppCompletionContext;
import org.zodiac.core.bootstrap.loadbalancer.AppLoadBalancerLifecycle;
import org.zodiac.core.bootstrap.loadbalancer.AppRequest;
import org.zodiac.core.bootstrap.loadbalancer.AppResponse;
import org.zodiac.core.bootstrap.loadbalancer.TimedAppRequestContext;
import org.zodiac.core.loadbalancer.support.AppLoadBalancerClientFactory;

public class StatsAppLoadBalancerLifecycle implements AppLoadBalancerLifecycle {

    private static final String REQUESTS_METRICS_PREFIX = String.format("%s.requests", AppLoadBalancerClientFactory.NAMESPACE);

    private final MeterRegistry meterRegistry;

    private final ConcurrentHashMap activeRequestsPerInstance = new ConcurrentHashMap<>();

    public StatsAppLoadBalancerLifecycle(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    @Override
    public boolean supports(Class requestContextClass, Class responseClass, Class serverTypeClass) {
        return AppInstance.class.isAssignableFrom(serverTypeClass);
    }

    @Override
    public void onStart(AppRequest request) {
    }

    @Override
    public void onStartRequest(AppRequest request, AppResponse lbResponse) {
        if (request.getContext() instanceof TimedAppRequestContext) {
            ((TimedAppRequestContext)request.getContext()).setRequestStartTime(System.nanoTime());
        }
        if (!lbResponse.hasServer()) {
            return;
        }
        AppInstance AppInstance = lbResponse.getServer();
        AtomicLong activeRequestsCounter = activeRequestsPerInstance.computeIfAbsent(AppInstance, instance -> {
            AtomicLong createdCounter = new AtomicLong();
            Gauge.builder(String.format("%s.active", REQUESTS_METRICS_PREFIX), () -> createdCounter).tags(AppLoadBalancerTags.buildInstanceTags(AppInstance))
                .register(meterRegistry);
            return createdCounter;
        });
        activeRequestsCounter.incrementAndGet();
    }

    @Override
    public void onComplete(AppCompletionContext completionContext) {
        long requestFinishedTimestamp = System.nanoTime();
        if (AppCompletionContext.Status.DISCARD.equals(completionContext.status())) {
            Counter.builder(String.format("%s.discard", REQUESTS_METRICS_PREFIX)).tags(AppLoadBalancerTags.buildDiscardedRequestTags(completionContext))
                .register(meterRegistry).increment();
            return;
        }
        AppInstance instance = completionContext.getLoadBalancerResponse().getServer();
        AtomicLong activeRequestsCounter = activeRequestsPerInstance.get(instance);
        if (activeRequestsCounter != null) {
            activeRequestsCounter.decrementAndGet();
        }
        Object loadBalancerRequestContext = completionContext.getLoadBalancerRequest().getContext();
        if (requestHasBeenTimed(loadBalancerRequestContext)) {
            if (AppCompletionContext.Status.FAILED.equals(completionContext.status())) {
                Timer.builder(String.format("%s.failed", REQUESTS_METRICS_PREFIX)).tags(AppLoadBalancerTags.buildFailedRequestTags(completionContext))
                    .register(meterRegistry).record(
                        requestFinishedTimestamp
                            - ((TimedAppRequestContext)loadBalancerRequestContext).getRequestStartTime(),
                        TimeUnit.NANOSECONDS);
                return;
            }
            Timer.builder(String.format("%s.success", REQUESTS_METRICS_PREFIX)).tags(AppLoadBalancerTags.buildSuccessRequestTags(completionContext))
                .register(meterRegistry).record(
                    requestFinishedTimestamp
                        - ((TimedAppRequestContext)loadBalancerRequestContext).getRequestStartTime(),
                    TimeUnit.NANOSECONDS);
        }
    }

    private boolean requestHasBeenTimed(Object loadBalancerRequestContext) {
        return loadBalancerRequestContext instanceof TimedAppRequestContext
            && (((TimedAppRequestContext)loadBalancerRequestContext).getRequestStartTime() != 0L);
    }

}