Metrics is a Java library which gives you unparalleled insight into what your code does in production.
Metrics provides a powerful toolkit of ways to measure the behavior of critical components in your production environment.
簡單的說Metrics是一個監(jiān)控系統(tǒng)性能指標的一個Java庫。Cassandra使用它進行指標統(tǒng)計律罢。
在Maven的pom.xml中引入Metrics:
<dependencies>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>${metrics.version}</version>
</dependency>
</dependencies>
Metrics使用非常簡單桩了,首先是Metrics的核心類
MetricRegistry類
The centerpiece of Metrics is the MetricRegistry class, which is the container for all your application’s metrics. Go ahead and create a new one:
我們一般直接new出Registry對象就可以完成Registry的創(chuàng)建风罩。
final MetricRegistry metrics = new MetricRegistry();
MetricRegistry
class中維護了一個Metric
集合和一個MetricRegistryListener
列表
private final ConcurrentMap<String, Metric> metrics;
private final List<MetricRegistryListener> listeners;
/**
* Creates a new {@link MetricRegistry}.
*/
public MetricRegistry() {
this.metrics = buildMap();
this.listeners = new CopyOnWriteArrayList<MetricRegistryListener>();
}
Metric
的注冊、快速創(chuàng)建潘拱、獲取等等的基本功能东臀,都會維護Metric
集合坯苹。
// 注冊
public <T extends Metric> T register(String name, T metric) throws IllegalArgumentException {
if (metric instanceof MetricSet) {
registerAll(name, (MetricSet) metric);
} else {
final Metric existing = metrics.putIfAbsent(name, metric);
if (existing == null) {
onMetricAdded(name, metric);
} else {
throw new IllegalArgumentException("A metric named " + name + " already exists");
}
}
return metric;
}
// 創(chuàng)建+注冊 or 獲取
public Meter meter(String name) {
return getOrAdd(name, MetricBuilder.METERS);
}
public Timer timer(String name) {
return getOrAdd(name, MetricBuilder.TIMERS);
}
// 獲取
public SortedMap<String, Gauge> getGauges() {
return getGauges(MetricFilter.ALL);
}
// etc
JmxReporter
strat的時候會向MetricRegistry
注冊一個JmxListener
class逢防,MetricRegistry
會將它維護到MetricRegistryListener
列表中叶沛。 JmxReporter
會在后面介紹到,它是統(tǒng)計結(jié)果的輸出控制類胞四。MetricRegistry
會在Metric
add or remove時將Metric
傳遞給所有的JmxListener
恬汁。JmxListener
是Metric
的真正使用者。
JmxReporter:
private final JmxListener listener;
private JmxReporter(MBeanServer mBeanServer,
String domain,
MetricRegistry registry,
MetricFilter filter,
MetricTimeUnits timeUnits,
ObjectNameFactory objectNameFactory) {
this.registry = registry;
this.listener = new JmxListener(mBeanServer, domain, filter, timeUnits, objectNameFactory);
}
public void start() {
registry.addListener(listener);
}
MetricRegistry:
private void onMetricAdded(String name, Metric metric) {
for (MetricRegistryListener listener : listeners) {
notifyListenerOfAddedMetric(listener, metric, name);
}
}
Reporter接口
它是Metrics輸出控制類的統(tǒng)一接口辜伟,它將采集的數(shù)據(jù)展現(xiàn)到不同的位置氓侧。Metrics提供兩個實現(xiàn)類JmxReporter
和ScheduledReporter
。
JmxReporter
會通過JMX報告指標导狡,它需要依賴metrics-jmx包
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jmx</artifactId>
<version>${metrics.version}</version>
</dependency>
final JmxReporter reporter = JmxReporter.forRegistry(registry).build();
reporter.start();
ScheduledReporter
顧名思義约巷,會定時的輸出報表。ConsoleReporter
旱捧、Slf4jReporter
等等Metric自帶的所以Reporter都是繼承于它独郎,一般我們自定義的Reporter也會繼承它。
A Console Reporter is exactly what it sounds like - report to the console. This reporter will print every second.
ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(1, TimeUnit.SECONDS);
Metrics
最后來看下Metrics提供了五個基本的Metrics :
- Meters(TPS)
A meter measures the rate of events over time (e.g., “requests per second”). In addition to the mean rate, meters also track 1-, 5-, and 15-minute moving averages.
使用起來非常簡單枚赡,這里暫不多介紹氓癌,只從官網(wǎng)復制了些實例代碼展示下如何使用:
private final MetricRegistry metrics = new MetricRegistry();
private final Meter requests = metrics.meter("requests");
public void handleRequest(Request request, Response response) {
requests.mark();
// etc
}
結(jié)果:
19-6-11 10:09:32 ===============================================================
-- Meters ----------------------------------------------------------------------
meter
count = 56
mean rate = 1.00 events/second
1-minute rate = 1.00 events/second
5-minute rate = 1.00 events/second
15-minute rate = 1.00 events/second
還有一些常用方法:
@Override
public long getCount() {
return count.sum();
}
@Override
public double getOneMinuteRate() {
tickIfNecessary();
return m1Rate.getRate(TimeUnit.SECONDS);
}
// etc
- Gauges
如果說上面Meters是一個動態(tài)的統(tǒng)計的話,Gauges就是一個靜態(tài)的統(tǒng)計贫橙。它能統(tǒng)計出關(guān)注對象當時的狀態(tài)贪婉。
A gauge is an instantaneous measurement of a value. For example, we may want to measure the number of pending jobs in a queue:
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();
}
});
}
}
MetricRegistry.name(QueueManager.class, "jobs", "size");
它的效率受時間復雜度影響
For most queue and queue-like structures, you won’t want to simply return queue.size(). Most of java.util and java.util.concurrent have implementations of #size() which are O(n), which means your gauge will be slow (potentially while holding a lock).
- Counters
類似于一個AtomicLong
。
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();
}
官方推薦使用#counter(String)
代替#register(String, Metric)
創(chuàng)建卢肃。
As you can see, the API for counters is slightly different: #counter(String) instead of #register(String, Metric). While you can use register and create your own Counter instance, #counter(String) does all the work for you, and allows you to reuse metrics with the same name.
Also, we’ve statically imported MetricRegistry’s name method in this scope to reduce clutter.
- Histograms(直方圖)
直方圖是一種用于統(tǒng)計數(shù)據(jù)的圖表疲迂,Histogram
能統(tǒng)計數(shù)據(jù)的最小值、最大值莫湘、平均值等尤蒿,還有測量中位數(shù)、75位幅垮、90位腰池、95位、98位军洼、99位和99.9巩螃。
A histogram measures the statistical distribution of values in a stream of data. In addition to minimum, maximum, mean, etc., it also measures median, 75th, 90th, 95th, 98th, 99th, and 99.9th percentiles.
private final Histogram responseSizes = metrics.histogram(name(RequestHandler.class, "response-sizes"));
public void handleRequest(Request request, Response response) {
// etc
responseSizes.update(response.getContent().length);
}
有一點在官網(wǎng)上特別提醒:歷史數(shù)據(jù)的大小之間影響到了Histogram
的響應(yīng)速度,所以Histogram
都需要設(shè)定了默認大小匕争。
This histogram will measure the size of responses in bytes.
Histogram
基本功能都是通過內(nèi)部的Reservoir
來實現(xiàn)的避乏。使用#histogram(String)
來創(chuàng)建會默認使用ExponentiallyDecayingReservoir
,并且他的默認大小是1028甘桑。
MetricBuilder<Histogram> HISTOGRAMS = new MetricBuilder<Histogram>() {
@Override
public Histogram newMetric() {
return new Histogram(new ExponentiallyDecayingReservoir());
}
@Override
public boolean isInstance(Metric metric) {
return Histogram.class.isInstance(metric);
}
};
- Timers
Timer
與Meter
類似拍皮,但它的功能比Meter
強大很多歹叮。Meter
只統(tǒng)計次數(shù),Timer
會統(tǒng)計過程的耗時铆帽,并提供更豐富的輸出統(tǒng)計咆耿。
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();
}
}
在使用上,我們需在計時開始處創(chuàng)建Context
爹橱,出口處調(diào)用#context.stop()
萨螺。
簡單看下源碼,我們能發(fā)現(xiàn)Timer
內(nèi)部其實就是使用 Meter
和Histogram
來實現(xiàn)的愧驱。
/**
* Creates a new {@link Timer} that uses the given {@link Reservoir} and {@link Clock}.
*
* @param reservoir the {@link Reservoir} implementation the timer should use
* @param clock the {@link Clock} implementation the timer should use
*/
public Timer(Reservoir reservoir, Clock clock) {
this.meter = new Meter(clock);
this.clock = clock;
this.histogram = new Histogram(reservoir);
}
private void update(long duration) {
if (duration >= 0) {
histogram.update(duration);
meter.mark();
}
}
Health Checks
Metrics還提供了服務(wù)的集中健康檢查慰技。
這需要在Maven的pom.xml中引入新的Metrics包:
<dependencies>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-healthchecks</artifactId>
<version>${metrics.version}</version>
</dependency>
</dependencies>
基本使用流程:
- 創(chuàng)建各種健康檢查的
HealthCheck
。
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());
}
}
}
- 將
HealthCheck
注冊到HealthCheckRegistry
。
final HealthCheckRegistry healthChecks = new HealthCheckRegistry();
healthChecks.register("postgres", new DatabaseHealthCheck(database));
- 運行
HealthCheckRegistry
:#runHealthChecks()
。
final Map<String, HealthCheck.Result> results = healthChecks.runHealthChecks();
- 通過遍歷返回值判斷健康狀態(tài)起意。
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();
}
}
}
JVM Instrumentation
這是Metrics中對于jvm的監(jiān)控。
The
metrics-jvm
module contains a number of reusable gauges and metric sets which allow you to easily instrument JVM internals.
它需要引入對于Jar包:
<dependencies>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jvm</artifactId>
<version>${metrics.version}</version>
</dependency>
</dependencies>
Metrics提供了各種jvm的指標metric
和MetricSet
子類邑雅,在子類對ManagementFactory
各種獲取屬性進行了分裝,來實現(xiàn)對jvm的監(jiān)控。
/**
* Creates a new set of gauges using the default MXBeans.
* Caches the information for the given interval and time unit.
*
* @param interval cache interval
* @param unit cache interval time unit
*/
public CachedThreadStatesGaugeSet(long interval, TimeUnit unit) {
this(ManagementFactory.getThreadMXBean(), new ThreadDeadlockDetector(), interval, unit);
}
public ClassLoadingGaugeSet() {
this(ManagementFactory.getClassLoadingMXBean());
}
/**
* Creates a new gauge using the platform OS bean.
*/
public FileDescriptorRatioGauge() {
this(ManagementFactory.getOperatingSystemMXBean());
}
// etc
它支持的指標包括:
- 運行所有支持的垃圾收集器的計數(shù)和已用時間
- 所有內(nèi)存池的內(nèi)存使用情況,包括堆外內(nèi)存
- 線程狀態(tài)的細分柒爸,包括死鎖
- 文件描述符用法
- 緩沖池大小和利用率
BufferPoolMetricSet
:JVM的直接緩沖池和映射緩沖池的數(shù)量統(tǒng)計。
ThreadStatesGaugeSet
:檢測處于不同狀態(tài)的線程數(shù)和死鎖檢測的Gauge事扭。
CachedThreadStatesGaugeSet
:同上揍鸟,多創(chuàng)建了一個CachedGauge
。
ClassLoadingGaugeSet
:JVM類加載器的Gauge句旱。
FileDescriptorRatioGauge
:是對于操作系統(tǒng)FileDescriptor的一個統(tǒng)計,可以用來監(jiān)控IO流的釋放等等晰奖。
GarbageCollectorMetricSet
:可以統(tǒng)計GC的次數(shù)和使用時間谈撒。
JmxAttributeGauge
:主要統(tǒng)計JVM的runtime,還在Gauge獲取了JVM的名稱匾南、供應(yīng)商信息啃匿。
MemoryUsageGaugeSet
:對于內(nèi)存的監(jiān)控(包含堆內(nèi)內(nèi)存和堆外內(nèi)存)。