天天看点

Metrics介绍和Spring的集成(转)引用Metric库RegistryGauge (仪表)Counter (计数器)Meter ()Histogram (直方图)Timer (计时器)Health Check (健康检查)JMX报表HTTP报表其它报表MetricSet一些模块第三方库Metrics for Spring

转自:http://blog.csdn.net/smallnest/article/details/38491507

http://colobu.com/2014/08/08/Metrics-and-Spring-Integration/

Metrics可以为你的代码的运行提供无与伦比的洞察力。作为一款监控指标的度量类库,它提供了很多模块可以为第三方库或者应用提供辅助统计信息, 比如Jetty, Logback, Log4j, Apache HttpClient, Ehcache, JDBI, Jersey, 它还可以将度量数据发送给Ganglia和Graphite以提供图形化的监控。

Metrics提供了Gauge、Counter、Meter、Histogram、Timer等度量工具类以及Health Check功能。

Metrics Graphite dashboard

引用Metric库

将metrics-core加入到maven pom.xml中:

1

2

3

4

5

6

7

<dependencies>

<dependency>

<groupId>com.codahale.metrics</groupId>

<artifactId>metrics-core</artifactId>

<version>${metrics.version}</version>

</dependency>

</dependencies>

metrics.version

 设置为metrics最新的版本。

现在你可以在你的程序代码中加入一些度量了。

Registry

Metric的中心部件是

MetricRegistry

。 它是程序中所有度量metric的容器。让我们接着在代码中加入一行:

final MetricRegistry metrics = new MetricRegistry();
           

Gauge (仪表)

Gauge

代表一个度量的即时值。 当你开汽车的时候, 当前速度是Gauge值。 你测体温的时候, 体温计的刻度是一个Gauge值。 当你的程序运行的时候, 内存使用量和CPU占用率都可以通过Gauge值来度量。

比如我们可以查看一个队列当前的size。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class QueueManager {

private final Queue queue;

public QueueManager(MetricRegistry metrics, String name) {

this.queue = new Queue();

metrics.register(MetricRegistry.name(QueueManager.class, name, "size"),

new Gauge<Integer>() {

@Override

public Integer getValue() {

return queue.size();

}

});

}

}

registry

 中每一个

metric

都有唯一的名字。 它可以是以.连接的字符串。 如”things.count” 和 “com.colobu.Thing.latency”。 

MetricRegistry

 提供了一个静态的辅助方法用来生成这个名字:

1 MetricRegistry.name(QueueManager.class, "jobs", "size")

生成的name为

com.colobu.QueueManager.jobs.size

实际编程中对于队列或者类似队列的数据结构,你不会简单的度量

queue.size()

, 因为在Java.util和java.util.concurrent包中大部分的queue的#size是O(n),这意味的调用此方法会有性能的问题, 更深一步,可能会有lock的问题。

RatioGauge可以计算两个Gauge的比值。 Meter和Timer可以参考下面的代码创建。下面的代码用来计算计算命中率 (hit/call)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public class CacheHitRatio extends RatioGauge {

private final Meter hits;

private final Timer calls;

public CacheHitRatio(Meter hits, Timer calls) {

this.hits = hits;

this.calls = calls;

}

@Override

public Ratio getValue() {

return Ratio.of(hits.oneMinuteRate(),

calls.oneMinuteRate());

}

}

CachedGauge可以缓存耗时的测量。DerivativeGauge可以引用另外一个Gauage。

Counter (计数器)

Counter

是一个

AtomicLong

实例, 可以增加或者减少值。 例如,可以用它来计数队列中加入的Job的总数。

1

2

3

4

5

6

7

8

9

10

11

private final Counter pendingJobs = metrics.counter(name(QueueManager.class, "pending-jobs"));

public void addJob(Job job) {

pendingJobs.inc();

queue.offer(job);

}

public Job takeJob() {

pendingJobs.dec();

return queue.take();

}

和上面Gauage不同,这里我们使用的是metrics.counter方法而不是metrics.register方法。 使用metrics.counter更简单。

Meter ()

Meter

用来计算事件的速率。 例如 request per second。 还可以提供1分钟,5分钟,15分钟不断更新的平均速率。

1

2

3

4

5

6

private final Meter requests = metrics.meter(name(RequestHandler.class, "requests"));

public void handleRequest(Request request, Response response) {

requests.mark();

// etc

}

Histogram (直方图)

Histogram

可以为数据流提供统计数据。 除了最大值,最小值,平均值外,它还可以测量 中值(median),百分比比如XX%这样的Quantile数据 。

1

2

3

4

5

6

private final Histogram responseSizes = metrics.histogram(name(RequestHandler.class, "response-sizes");

public void handleRequest(Request request, Response response) {

// etc

responseSizes.update(response.getContent().length);

}

这个例子用来统计response的字节数。

Metrics提供了一批的Reservoir实现,非常有用。例如SlidingTimeWindowReservoir 用来统计最新N个秒(或其它时间单元)的数据。

Timer (计时器)

Timer

用来测量一段代码被调用的速率和用时。

1

2

3

4

5

6

7

8

9

10

11

private final Timer responses = metrics.timer(name(RequestHandler.class, "responses"));

public String handleRequest(Request request, Response response) {

final Timer.Context context = responses.time();

try {

// etc;

return "OK";

} finally {

context.stop();

}

}

这段代码用来计算中间的代码用时以及request的速率。

Health Check (健康检查)

Metric

还提供了服务健康检查能力, 由

metrics-healthchecks

模块提供。

先创建一个

HealthCheckRegistry

实例。

1 final HealthCheckRegistry healthChecks = new HealthCheckRegistry();

再实现一个

HealthCheck

子类, 用来检查数据库的状态。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public class DatabaseHealthCheck extends HealthCheck {

private final Database database;

public DatabaseHealthCheck(Database database) {

this.database = database;

}

@Override

public HealthCheck.Result check() throws Exception {

if (database.isConnected()) {

return HealthCheck.Result.healthy();

} else {

return HealthCheck.Result.unhealthy("Cannot connect to " + database.getUrl());

}

}

}

注册一下。

1 healthChecks.register("mysql", new DatabaseHealthCheck(database));

最后运行健康检查并查看检查结果。

1

2

3

4

5

6

7

8

9

10

11

12

final Map<String, HealthCheck.Result> results = healthChecks.runHealthChecks();

for (Entry<String, HealthCheck.Result> entry : results.entrySet()) {

if (entry.getValue().isHealthy()) {

System.out.println(entry.getKey() + " is healthy");

} else {

System.err.println(entry.getKey() + " is UNHEALTHY: " + entry.getValue().getMessage());

final Throwable e = entry.getValue().getError();

if (e != null) {

e.printStackTrace();

}

}

}

Metric

内置一个ThreadDeadlockHealthCheck, 它使用java内置的线程死锁检查方法来检查程序中是否有死锁。

JMX报表

通过JMX报告Metric。

1

2

final JmxReporter reporter = JmxReporter.forRegistry(registry).build();

reporter.start();

一旦启动, 所有registry中注册的metric都可以通过JConsole或者VisualVM查看 (通过MBean插件)。

通过JMX查看

HTTP报表

Metric也提供了一个servlet (AdminServlet)提供JSON风格的报表。它还提供了单一功能的servlet (MetricsServlet, HealthCheckServlet, ThreadDumpServlet, PingServlet)。

你需要在pom.xml加入metrics-servlets。

1

2

3

4

5

<dependency>

<groupId>com.codahale.metrics</groupId>

<artifactId>metrics-servlets</artifactId>

<version>${metrics.version}</version>

</dependency>

其它报表

除了JMX和HTTP, metric还提供其它报表。

  • STDOUT, using ConsoleReporter from metrics-core

1

2

3

4

5

final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)

.convertRatesTo(TimeUnit.SECONDS)

.convertDurationsTo(TimeUnit.MILLISECONDS)

.build();

reporter.start(1, TimeUnit.MINUTES);

  • CSV files, using CsvReporter from metrics-core

1

2

3

4

5

6

final CsvReporter reporter = CsvReporter.forRegistry(registry)

.formatFor(Locale.US)

.convertRatesTo(TimeUnit.SECONDS)

.convertDurationsTo(TimeUnit.MILLISECONDS)

.build(new File("~/projects/data/"));

reporter.start(1, TimeUnit.SECONDS);

  • SLF4J loggers, using Slf4jReporter from metrics-core

1

2

3

4

5

6

final Slf4jReporter reporter = Slf4jReporter.forRegistry(registry)

.outputTo(LoggerFactory.getLogger("com.example.metrics"))

.convertRatesTo(TimeUnit.SECONDS)

.convertDurationsTo(TimeUnit.MILLISECONDS)

.build();

reporter.start(1, TimeUnit.MINUTES);

  • Ganglia, using GangliaReporter from metrics-ganglia

1

2

3

4

5

6

final GMetric ganglia = new GMetric("ganglia.example.com", 8649, UDPAddressingMode.MULTICAST, 1);

final GangliaReporter reporter = GangliaReporter.forRegistry(registry)

.convertRatesTo(TimeUnit.SECONDS)

.convertDurationsTo(TimeUnit.MILLISECONDS)

.build(ganglia);

reporter.start(1, TimeUnit.MINUTES);

  • Graphite, using GraphiteReporter from metrics-graphite

MetricSet

可以将一组Metric组织成一组便于重用。

1

2

3

4

5

6

7

8

final Graphite graphite = new Graphite(new InetSocketAddress("graphite.example.com", 2003));

final GraphiteReporter reporter = GraphiteReporter.forRegistry(registry)

.prefixedWith("web1.example.com")

.convertRatesTo(TimeUnit.SECONDS)

.convertDurationsTo(TimeUnit.MILLISECONDS)

.filter(MetricFilter.ALL)

.build(graphite);

reporter.start(1, TimeUnit.MINUTES);

一些模块

  • metrics-json提供了json格式的序列化。

    以及为其它库提供度量的能力

  • metrics-ehcache
  • metrics-httpclient
  • metrics-jdbi
  • metrics-jersey
  • metrics-jetty
  • metrics-log4j
  • metrics-logback
  • metrics-jvm
  • metrics-servlet 注意不是metrics-servlets

第三方库

  • metrics-librato 提供Librato Metrics报表
  • Metrics Spring Integration 提供了Spring的集成
  • sematext-metrics-reporter 提供了SPM报表.
  • wicket-metrics提供Wicket应用.
  • metrics-guice 提供Guice集成.
  • metrics-scala 提供了为Scala优化的API.

这里重点介绍一下Metrics for spring

Metrics for Spring

这个库为Spring增加了Metric库, 提供基于XML或者注解方式。

  • 可以使用注解创建metric和代理类。 @Timed, @Metered, @ExceptionMetered, @Counted
  • 为注解了 @Gauge 和 @CachedGauge的bean注册Gauge
  • 为@Metric注解的字段自动装配
  • 注册HealthCheck
  • 通过XML配置产生报表
  • 通过XML注册metric和metric组

你需要在pom.xml加入

1

2

3

4

5

<dependency>

<groupId>com.ryantenney.metrics</groupId>

<artifactId>metrics-spring</artifactId>

<version>3.0.1</version>

</dependency>

基本用法

  1. XML风格的配置

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:metrics="http://www.ryantenney.com/schema/metrics"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

http://www.ryantenney.com/schema/metrics

http://www.ryantenney.com/schema/metrics/metrics-3.0.xsd">

<!-- Registry should be defined in only one context XML file -->

<metrics:metric-registry id="metrics" />

<!-- annotation-driven must be included in all context files -->

<metrics:annotation-driven metric-registry="metrics" />

<!-- (Optional) Registry should be defined in only one context XML file -->

<metrics:reporter type="console" metric-registry="metrics" period="1m" />

<!-- (Optional) The metrics in this example require the metrics-jvm jar-->

<metrics:register metric-registry="metrics">

<bean metrics:name="jvm.gc" class="com.codahale.metrics.jvm.GarbageCollectorMetricSet" />

<bean metrics:name="jvm.memory" class="com.codahale.metrics.jvm.MemoryUsageGaugeSet" />

<bean metrics:name="jvm.thread-states" class="com.codahale.metrics.jvm.ThreadStatesGaugeSet" />

<bean metrics:name="jvm.fd.usage" class="com.codahale.metrics.jvm.FileDescriptorRatioGauge" />

</metrics:register>

<!-- Beans and other Spring config -->

</beans>

  1. java注解的方式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

import java.util.concurrent.TimeUnit;

import org.springframework.context.annotation.Configuration;

import com.codahale.metrics.ConsoleReporter;

import com.codahale.metrics.MetricRegistry;

import com.codahale.metrics.SharedMetricRegistries;

import com.ryantenney.metrics.spring.config.annotation.EnableMetrics;

import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter;

@Configuration

@EnableMetrics

public class SpringConfiguringClass extends MetricsConfigurerAdapter {

@Override

public void configureReporters(MetricRegistry metricRegistry) {

ConsoleReporter

.forRegistry(metricRegistry)

.build()

.start(1, TimeUnit.MINUTES);

}

}