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

org.zodiac.actuate.feign.FeignClientMetrics Maven / Gradle / Ivy

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

import feign.Client;
import feign.Request;
import feign.Response;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;

import javax.annotation.Nullable;

import org.zodiac.commons.util.Colls;
import org.zodiac.feign.core.constants.FeignConstants;
import org.zodiac.feign.core.util.FeignUtil;
import org.zodiac.sdk.nio.http.common.HttpHeaders;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

public class FeignClientMetrics {

    private MeterRegistry registry;
    private final String requestsMetricName;
    private final Iterable extraTags;

    public FeignClientMetrics(String requestsMetricName, Iterable extraTags) {
        this(null, requestsMetricName, extraTags);
    }

    public FeignClientMetrics(MeterRegistry registry, String requestsMetricName, Iterable extraTags) {
        this.registry = registry;
        this.requestsMetricName = requestsMetricName;
        this.extraTags = extraTags;
    }

    public void setRegistry(MeterRegistry registry) {
        this.registry = registry;
    }

    public static Builder builder(String name) {
        return new Builder(name);
    }

    /**
     * 代理FeignClient。
     *
     * @param client 客户端对象
     * @return 被代理的客户端对象
     */
    public Client delegate(Client client) {
        return ((request, options) -> {
            Clock clock = this.registry.config().clock();
            long startTime = clock.monotonicTime();
            String originalUri = getOriginalUri(request);
            if (originalUri != null) {
                Map> newHeaders = Colls.map(request.headers());
                newHeaders.remove(FeignConstants.FEIGN_ANNOTATION_PATH);
                request = Request.create(request.httpMethod(), request.url(), Collections.unmodifiableMap(newHeaders),
                    request.body(), request.charset(), null);
            }
            Response response = null;
            Exception exception = null;
            try {
                response = client.execute(request, options);
                return response;
            } catch (Exception ex) {
                exception = ex;
                throw ex;
            } finally {
                record(clock.monotonicTime() - startTime, originalUri, request, response, exception);
            }
        });
    }

    /**
     * 从请求对象中取得原始URI。
     *
     * @param request 请求对象
     * @return 原始URI分片
     */
    @Nullable
    private String getOriginalUri(Request request) {
        /*取得原始URI模板。*/
        Collection originalUrlHeaders = request.headers().get(FeignConstants.FEIGN_ANNOTATION_PATH);
        if (originalUrlHeaders != null && !originalUrlHeaders.isEmpty()) {
            return FeignUtil.transferToRaw(originalUrlHeaders.iterator().next());
        }
        return null;
    }

    /**
     * 从请求对象中取得Host。
     *
     * @param request 请求对象
     * @return Host信息
     */
    @Nullable
    private String getHostFromHeader(Request request) {
        /*尝试从头中获取Host。*/
        Collection hostHeaders = request.headers().get(HttpHeaders.HOST);
        if (hostHeaders != null && !hostHeaders.isEmpty()) {
            return hostHeaders.iterator().next();
        }
        return null;
    }

    /**
     * 记录指标项。
     *
     * @param costTime 响应时间,单位毫秒
     * @param originalUri 原始请求路径
     * @param request 请求对象
     * @param response 应答对象
     * @param exception 异常对象
     */
    private void record(long costTime, String originalUri, Request request, Response response, Exception exception) {
        try {
            URL uri = new URL(request.url());
            Iterable tags = Tags.concat(extraTags, Tags.of(
                "host", Optional.ofNullable(getHostFromHeader(request)).orElse(uri.getHost()),
                "remote", uri.getHost() + (uri.getPort() > 0 ? ":" + uri.getPort() : ""),
                "method", request.httpMethod().name(),
                "uri", originalUri != null ? originalUri : "Unknown",
                "status", response != null ? String.format("%d", response.status()) : "None",
                "exception", exception != null ? exception.getClass().getSimpleName() : "None"
            ));

            Timer.builder(this.requestsMetricName)
                .tags(tags)
                .description("Timer of Feign client")
                .register(registry)
                .record(costTime, TimeUnit.NANOSECONDS);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    public static class Builder {
        private String name;
        private Iterable tags = Collections.emptyList();

        Builder(String name) {
            this.name = name;
        }

        public Builder tags(Iterable tags) {
            this.tags = tags;
            return this;
        }

        public FeignClientMetrics build() {
            return new FeignClientMetrics(name, tags);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy