Metrics的开发代码与配置

依赖jar的pom配置

<!--metrics相关-->
        <dependency>
            <groupId>io.dropwizard.metrics</groupId>
            <artifactId>metrics-core</artifactId>
            <version>4.0.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.dropwizard.metrics/metrics-jvm -->
        <dependency>
            <groupId>io.dropwizard.metrics</groupId>
            <artifactId>metrics-jvm</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.davidb</groupId>
            <artifactId>metrics-influxdb</artifactId>
            <version>1.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <scope>compile</scope>
        </dependency>

 

spring中bean配置

    <context:component-scan base-package="com.example.common.metrics"/>
    <bean id="metricRegistry" class="com.codahale.metrics.MetricRegistry"/>

 

基础工具:MetricsBase.java

import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MetricsBase {
    private final Map<String, Meter> meterMap = new ConcurrentHashMap<>();
    private final Map<String, Timer> timerMap = new ConcurrentHashMap<>();
    private final Map<String, Histogram> histogramMap = new ConcurrentHashMap<>();

    public static String keyGen(String meterMeasurements) {
        String[] parent = parent();
        return MetricRegistry.name(parent[0], parent[1], meterMeasurements);
    }

    /**
     * 获取Meter实例
     *
     * @param metricsKey
     * @return
     */
    public Meter getMeter(String metricsKey, MetricRegistry metricRegistry) {
        Meter m = meterMap.get(metricsKey);
        if (m != null) {
            return m;
        }
        synchronized (MetricsBase.class) {
            Meter metrics = meterMap.get(metricsKey);
            if (metrics != null) {
                return metrics;
            } else {
                Meter object = metricRegistry.meter(metricsKey);
                meterMap.putIfAbsent(metricsKey, object);
                return object;
            }
        }
    }

    /**
     * 获取Timer实例
     *
     * @param metricsKey
     * @return
     */
    public Timer getTimer(String metricsKey, MetricRegistry metricRegistry) {
        Timer t = timerMap.get(metricsKey);
        if (t != null) {
            return t;
        }
        synchronized (MetricsBase.class) {
            Timer timer = timerMap.get(metricsKey);
            if (timer != null) {
                return timer;
            } else {
                Timer object = metricRegistry.timer(metricsKey);
                timerMap.putIfAbsent(metricsKey, object);
                return object;
            }
        }
    }

    /**
     * Histogram 直方图数据
     * @param metricsKey
     * @return
     */
    public Histogram getHistogram(String metricsKey, MetricRegistry metricRegistry) {
        Histogram t = histogramMap.get(metricsKey);
        if (t != null) {
            return t;
        }
        synchronized (MetricsBase.class) {
            Histogram histogram = histogramMap.get(metricsKey);
            if (histogram != null) {
                return histogram;
            } else {
                Histogram object = metricRegistry.histogram(metricsKey);
                histogramMap.putIfAbsent(metricsKey, object);
                return object;
            }
        }
    }


    public static String[] parent(){
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        StackTraceElement stackTraceElement = stackTrace[4];
        String className = stackTraceElement.getClassName();
        String []classNames = className.split("\\.");
        return new String[]{classNames[classNames.length-1], stackTraceElement.getMethodName()};
    }
}

 

调用方法:MetricsFactory.java

import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MetricsFactory extends MetricsBase{
    protected Logger log = LoggerFactory.getLogger(this.getClass());
    private final static String TIMER_MEASUREMENTS = "smsTimer";
    private final static String METER_MEASUREMENTS = "smsMeter";
    private final static String HISTOGRAM_MEASUREMENTS = "smsHistogram";

    private MetricsFactory() {
    }

    private static class MetricsFactoryInstance {
        private static final MetricsFactory INSTANCE = new MetricsFactory();
    }

    public static MetricsFactory getInstance() {
        return MetricsFactoryInstance.INSTANCE;
    }


    /**
     * 接口TPS统计
     * @param metricRegistry
     * @return
     */
    public void getMeter(MetricRegistry metricRegistry) {
        try {
            String metricKey = keyGen(METER_MEASUREMENTS);
            log.info("MetricsFactory.getMeter.metricKey:{}",metricKey);
            getMeter(metricKey, metricRegistry).mark();
        } catch (Exception e) {
            log.error("[NoticeCenter]create metrics(meter) error", e);
        }
    }

    /**
     * 接口耗时统计
     * @param metricRegistry
     * @return
     */
    public Timer.Context getTimer(MetricRegistry metricRegistry) {
        try {
            String metricKey = keyGen(TIMER_MEASUREMENTS);
            log.info("MetricsFactory.getTimer.metricKey:{}",metricKey);
            return getTimer(metricKey, metricRegistry).time();
        } catch (Exception e) {
            log.error("[NoticeCenter]create metrics(timer) error", e);
            return null;
        }
    }
    /**
     * 停止Metrics.Timer记录
     * @param context
     */
    public void stopTimer(Timer.Context context) {
        try {
            if (context != null) {
                context.stop();
            }
        } catch (Exception e) {
            log.error("[NoticeCenter]metrics(timer) stop error", e);
        }
    }

    /**
     * 直方图数据
     * @param metricRegistry
     * @return
     */
    public Histogram getHistogram(MetricRegistry metricRegistry) {
        try {
            String metricKey = keyGen(HISTOGRAM_MEASUREMENTS);
            return getHistogram(metricKey, metricRegistry);
        } catch (Exception e) {
            log.error("[NoticeCenter]create metrics(timer) error", e);
            return null;
        }
    }
}

 

初始化:APPMertics.java

import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.ScheduledReporter;
import com.codahale.metrics.jvm.*;
import metrics_influxdb.HttpInfluxdbProtocol;
import metrics_influxdb.InfluxdbReporter;
import metrics_influxdb.api.measurements.CategoriesMetricMeasurementTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Component
public class AppMetrics implements InitializingBean {

    private static final Logger log = LoggerFactory.getLogger(AppMetrics.class);

    @Autowired
    private MetricRegistry metricRegistry;

    @Value("${server.port}")
    private int port;

    @Value("${influxdb.reportJvm}")
    private boolean reportJvm;

    @Value("${influxdb.host}")
    private String influxHost;

//    @Value("${influxdb.user}")
//    private String influxUser;
//
//    @Value("${influxdb.passwd}")
//    private String influxPasswd;

    @Value("${influxdb.db}")
    private String influxDB;

    @Value("${influxdb.port}")
    private int influxPort;

    @Value("${metrics.reporterInterval}")
    private long reporterInterval;

    public void afterPropertiesSet() throws Exception {
        log.info("AppMetrics init ***********,port: {}, reportJvm: {}, influxdb: {}, influxPort: {},influxDB: {}", port,reportJvm, influxHost, influxPort, influxDB);
        if (reportJvm){
            initJVM();
        }

        if (StringUtils.isEmpty(influxHost)) {
            startConsoleReporter();
        } else {
            try {
                influxDbReporter();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void initJVM() {
        metricRegistry.register("jvm.gc", new GarbageCollectorMetricSet());
        metricRegistry.register("jvm.thread-state", new CachedThreadStatesGaugeSet(10, TimeUnit.SECONDS));
        metricRegistry.register("jvm.mem", new MemoryUsageGaugeSet());
        metricRegistry.register("jvm.attr", new JvmAttributeGaugeSet());
        metricRegistry.register("jvm.buffer-pool", new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer()));
        metricRegistry.register("jvm.fd.usage", new FileDescriptorRatioGauge());
    }


    private void influxDbReporter() throws Exception{
        log.info("use influxdb as reporter {}:{}, {}", influxHost, influxPort, influxDB);
        final ScheduledReporter reporter = InfluxdbReporter.forRegistry(metricRegistry)
//                .protocol(new HttpInfluxdbProtocol("http", influxHost, influxPort, influxUser, influxPasswd, influxDB))
                .protocol(new HttpInfluxdbProtocol(influxHost, influxPort, influxDB))
                .convertRatesTo(TimeUnit.SECONDS)
                .convertDurationsTo(TimeUnit.MILLISECONDS)
                .filter(MetricFilter.ALL)
                .skipIdleMetrics(false)
                .tag("client", Integer.toString(port))
                .tag("server", InetAddress.getLocalHost().getHostAddress())
                .transformer(new CategoriesMetricMeasurementTransformer("noticeName", "ruleName", "measurement"))
                .build();
        long initalDelay = getBeginTime().getTimeInMillis() - System.currentTimeMillis();
        long period = reporterInterval * 1000;
        reporter.start(initalDelay, period, TimeUnit.MILLISECONDS);
    }

    private void startConsoleReporter() {
        log.info("use console as reporter");
        ConsoleReporter reporter = ConsoleReporter.forRegistry(metricRegistry)
                .convertRatesTo(TimeUnit.SECONDS)
                .convertDurationsTo(TimeUnit.MILLISECONDS)
                .build();
        long initalDelay = getBeginTime().getTimeInMillis() - System.currentTimeMillis();
        long period = reporterInterval * 1000;
        reporter.start(initalDelay, period, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取Metrics报告时间:
     * Metrics报告时间设定为启动后1分钟0秒开始,
     * 保证所有机器上的数据的开始时间都是从某分钟开始
     *
     * @return
     */
    private Calendar getBeginTime() {
        Calendar beginTime = Calendar.getInstance();
        beginTime.setTime(new Date());
        beginTime.add(Calendar.MINUTE, 1);
        beginTime.set(Calendar.SECOND, 0);// 秒
        beginTime.set(Calendar.MILLISECOND, 0);// 毫秒
        return beginTime;
    }
}

 

以上工具类完成后在项目中就可以使用了!

项目中properties属性配置

#influxdb 配置参数
#是否上报JVM性能数据
influxdb.reportJvm=false
influxdb.host=${content.influxdb.host}
#当前服务端口
server.port=-1
#influxdb.port=${content.influxdb.port}
influxdb.port=8086

influxdb.user=root
influxdb.passwd=root

#influxdb.db=#{content.influxdb.name}
influxdb.db=metrics
#metrics数据上报时间间隔
metrics.reporterInterval=10

 工具类的使用demo:

@CrossOrigin(methods={RequestMethod.GET, RequestMethod.POST,RequestMethod.OPTIONS})
@Controller
@RequestMapping("/metrics")
public class MetricsController extends BaseController {
    private final static Logger logger = LoggerFactory.getLogger(MetricsController.class);

    @Autowired
    MetricRegistry metricRegistry;
    @ResponseBody()
    @ResponseStatus(HttpStatus.CREATED)
    @RequestMapping(value="/detailQuery",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.OPTIONS})
    public void detailQuery(
            HttpServletResponse response,
            @RequestParam(required = false) String jsonpcallback) {
        logger.info("metrics detailQuery");
        RpcResponseDTO<String> result = new RpcResponseDTO<>();
        result.setPayload("hello word");
        //接口耗时统计
        Timer.Context timerContext = MetricsFactory.getInstance().getTimer(metricRegistry);
      // excute method ... MetricsFactory.getInstance().stopTimer(timerContext); //接口TPS统计 MetricsFactory.getInstance().getMeter(metricRegistry); SpringMVCRpcHelper.renderJson(response, jsonpcallback, result); } }

 

参考文献

  Metrics+Influxdb+Grafana构建监控系统(可能全网最详)

上一篇:【运维面试】k8s的监控指标


下一篇:Prometheus 之 K8S系统组件服务监控