目錄
Metrics
介紹
監控工具。Metrics提供了強大的工具用於監控重要組件的行爲。Metrics依賴Jetty, Logback, Log4j, Apache HttpClient, Ehcache, JDBI, Jersey和Graphite,提供一個可視化程序執行情況。
Metrics可以統計TPS,方法執行次數,隊列情況,方法執行時間等,可以支持生成儀表盤格式,柱狀圖格式的數據。並且支持將此類數據輸出到控制檯、日誌文件、CSV格式文件中。也可以通過簡單配置提供返回JMX格式的接口。
使用
Metric Registries
Metric註冊中心是Metric工具的入口,用於提供創建各種監控工具和存儲這些監控工具。通常情況下一個應用只需要一個Metrics註冊中心,並且應用初始化時初始化註冊中心。如果應用需要多個Metric註冊中心,可以使用SharedMetricRegistries來管理。SharedMetricRegistries維護一個繼承自ConcurrentMap的REGISTRIES對象,key是註冊中心的名字,value是註冊中心實例。SharedMetricRegistries提供一些基本的維護註冊中心(添加和刪除註冊中心)方法。
如下是Spring Boot利用註冊方式初始化一個Metric Registries:
@Configuration
public class Configuration {
@Bean
MetricRegistry metricRegistry() {
return new MetricRegistry();
}
}
Metric Registries利用ConcurrentMap來存儲所有的監控,key是監控的名字,value是監控的實例。所以註冊中心要求每個監控的名字必須唯一。MetricRegistry提供了生成監控名稱的方法
/**
* Concatenates elements to form a dotted name, eliding any null values or empty strings.
*
* @param name the first element of the name
* @param names the remaining elements of the name
* @return {@code name} and {@code names} concatenated by periods
*/
public static String name(String name, String... names) {
final StringBuilder builder = new StringBuilder();
append(builder, name);
if (names != null) {
for (String s : names) {
append(builder, s);
}
}
return builder.toString();
}
官網推薦使用 類對象+業務對象+業務對象屬性來命名監控名稱,比如:
public static void main(String[] args) {
String name = MetricRegistry.name(Queue.class, "requests", "size");
}
Gauges
gauge是一種只監控統計一個唯獨的監控數據類型,比如可以統計程序啓動後接口被訪問了多少次:
@org.springframework.context.annotation.Configuration
public class Configuration {
@Bean
MetricRegistry metricRegistry() {
return new MetricRegistry();
}
// 配置控制檯輸出監控指標
@Bean
ConsoleReporter consoleReporter() {
ConsoleReporter reporter = ConsoleReporter.forRegistry(metricRegistry())
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(5, TimeUnit.SECONDS);
return reporter;
}
}
@RestController
public class GaugesController {
// test方法被訪問的次數
private int countTest = 0;
// register在項目啓動過程中已經被Spring初始化並加入到bean容器中
public GaugesController(MetricRegistry registry) {
registry.register("test", (Gauge<Integer>) () -> countTest);
}
@RequestMapping(value = "/gauges/test")
public String gauges() {
countTest++;
return String.valueOf(countTest);
}
}
控制檯每5秒鐘輸出結果,隨着每次點擊 test值不斷增加。本次點擊測試的輸入如下:
#第一次輸入(本人點擊了一次)
-- Gauges ----------------------------------------------------------------------
test
value = 1
# 第二次的5秒輸出(本人累計點擊7次)
-- Gauges ----------------------------------------------------------------------
test
value = 7
這裏只是舉了一個使用gauge的例子,Metrics有單獨提供用於統計次數的監控數據類型Counters。
gauges還有一種可以一個監控兩組數據比率的監控數據類型RatioGauge,以下列舉兩個接口調用次數的比例來練習RatioGauge的使用。
@RestController
public class GaugesController {
public GaugesController(MetricRegistry registry) {
registry.register("test", (Gauge<Integer>) () -> countTest);
// 註冊一個RatioGauge
RatioGauge ratioGauge = new RatioGauge() {
@Override
protected Ratio getRatio() {
// ratio返回 接口/gauges/ratio調用次數除以
return Ratio.of(countRatioGauges, countTest + countRatioGauges);
}
};
registry.register("test2", ratioGauge);
}
@GetMapping(value = "/gauges/test")
public String gauges() {
countTest++;
return String.valueOf(countTest);
}
// 增加一個接口,並統計調用次數
@GetMapping(value = "/gauges/ratio")
public String ratioGauges() {
countRatioGauges++;
return String.valueOf(countRatioGauges);
}
private int countTest = 0;
private int countRatioGauges = 0;
當/gauges/test請求訪問兩次,/gauges/ratio請求訪問6次 test2指標的輸出結果:
-- Gauges ----------------------------------------------------------------------
test2
value = 0.75
Gauges 提供一個能夠緩存指定時間的監控CachedGauge,以下是CachedGauge的例子:
// 註冊一個Cached Gauge
@Bean
CachedGauge<Long> cachedGauge(MetricRegistry registry) {
CachedGauge<Long> cachedGauge = new CachedGauge<Long>(10000, TimeUnit.MILLISECONDS) {
@Override
protected Long loadValue() {
try {
System.out.println("start CachedGauge before execute loadValue " + LocalDateTime.now());
// 獲取具體的內容
long value = longTimeValue();
System.out.println("start CachedGauge after execute loadValue " + LocalDateTime.now());
return value;
} catch (InterruptedException e) {
System.out.println(e);
return 0L;
}
}
};
registry.register("cached", cachedGauge);
return cachedGauge;
}
/**
* 睡眠2秒後返回一個隨機數
*/
private long longTimeValue() throws InterruptedException {
Thread.sleep(2000);
return random.nextInt(100);
}
private Random random = new Random();
/**
* 隨機數生成器
*/
private Random random;
Cached Guage可以在第一次獲取到結果後,將結果緩存。在緩存未失效前,如果需要獲取結果則直接返回緩存中的結果;如果緩存失效則再次執行獲取結果的任務,並緩存結果。
Gauges提供了一種可以自定義轉換監控數據的監控類型DerivativeGauge,一下是DerivativeGauge使用例子:
// 定義DerivativeGauge
public GaugesController(MetricRegistry registry) {
// 定義一個Gauge,用於統計接口gauges/test的訪問次數
Gauge<Long> gauge = () -> (long)countTest;
// 初始化DerivativeGauge,自定義統計接口訪問次數的指標如何轉成成其他指標
DerivativeGauge<Long, String> derivativeGauge = new DerivativeGauge<Long, String>(gauge) {
@Override
protected String transform(Long value) {
return String.format("這是訪問第%s", value);
}
};
registry.register("derivative", derivativeGauge);
}
@GetMapping(value = "/gauges/test")
public String gauges() {
// 非線程安全,只作爲一個演示例子
countTest++;
return String.valueOf(countTest);
}
// 記錄接口訪問次數
private int countTest = 0;
以下是控制檯輸出:
```
– Gauges -----------------------------------------------------------------–—
derivative
value = 這是訪問第3
```
Counter
Counter是一個計算器監控類型,基於LongAdder實現的計數,以下是使用的例子:
@RestController
public class CounterController {
public CounterController(MetricRegistry registry) {
//初始化一個Counter類型的監控
counter = new Counter();
registry.register("counter", counter);
}
@GetMapping("counter/add")
public long add() {
// 每次方法調用時自增一
counter.inc();
return counter.getCount();
}
@GetMapping("counter/minus")
public long method1() {
// 方法調用時自減一
counter.dec();
return counter.getCount();
}
/**
* 計算器
*/
private Counter counter;
counter/add 請求調用5次,counter/minus請求調用12次後監控輸出如下
-- Counters --------------------------------------------------------------------
counter
count = -7
Histograms
Histogram是用來統計一個監控指標在連續的時間緯度上的分佈情況的直方圖監控類型,監控的包括:次數,最小值,最大值,均值,標準差,中位數,75%、95%、98%、99%、99.9%分位值。Histogram只適用於小數據量下的計算。
Histogram在數據修改時,通過採樣將數據維護在一個小的、可管理的、在統計上代表整個數據樣本的存儲庫上,通過計算存儲庫上的數據得到統計結果。
使用例子如下:
@RestController
public class HistogramController {
public HistogramController(MetricRegistry registry) {
longAdder = new LongAdder();
histogram = registry.histogram(name(HistogramController.class, "counts"));
}
@GetMapping("histogram")
public long histogram() {
longAdder.increment();
long value = longAdder.longValue();
histogram.update(value);
return value;
}
private LongAdder longAdder;
private Histogram histogram;
}
Histogram常用於監控接口響應時間,緩存大小等場景。
Meters
Meters用於監控事件發生的頻率,比如每秒接口被調用的次數。一下是Meter的使用例子:
public MeterController(MetricRegistry registry) {
getRequests = registry.meter(name(MeterController.class, "meter-requests", "requests"));
}
@GetMapping("/meter/1")
public String meter() {
getRequests.mark();
return null;
}
@GetMapping("/meter/2")
public String meter2() {
getRequests.mark();
return null;
}
@GetMapping("/meter/3")
public String meter3() {
getRequests.mark();
return null;
}
快速訪問接口/meter/1,日誌輸出如下:
-- Meters ----------------------------------------------------------------------
com.jackframe.practice.metrics.meter.MeterController.meter-requests.requests
count = 17
mean rate = 1.70 events/second
1-minute rate = 1.28 events/second
5-minute rate = 1.22 events/second
15-minute rate = 1.21 events/second
Meters監控的是事件的觸發次數,每秒鐘觸發的中位數,1分鐘內/5分鐘內/15分鐘內每秒觸發的次數,經常用於監控接口訪問情況
Timers
Timers是Meters和Histogram的組合,使用如下:
public TimerController(MetricRegistry registry) {
timer = registry.timer(name(TimerController.class, "get-requests"));
}
@GetMapping("timer")
public String timer() {
final Timer.Context context = timer.time();
try {
System.out.println("execute task");
} finally {
context.stop();
}
return "";
}
private final Timer timer;
快速訪問/timer接口17次後,監控輸入如下:
-- Timers ----------------------------------------------------------------------
com.jackframe.practice.metrics.timer.TimerController.get-requests
count = 17
mean rate = 1.70 calls/second
1-minute rate = 0.61 calls/second
5-minute rate = 0.44 calls/second
15-minute rate = 0.41 calls/second
min = 0.03 milliseconds
max = 0.15 milliseconds
mean = 0.04 milliseconds
stddev = 0.03 milliseconds
median = 0.03 milliseconds
75% <= 0.03 milliseconds
95% <= 0.15 milliseconds
98% <= 0.15 milliseconds
99% <= 0.15 milliseconds
99.9% <= 0.15 milliseconds
監控輸出方式
Metrics監控結果輸出方式支持輸出控制檯,SLF4J(日誌文件),CSV(文件)。
以下是定一個每10秒一次將監控數據輸出到控制檯的輸出配置:
// 註冊一個控制檯輸出工具
ConsoleReporter reporter = ConsoleReporter.forRegistry(metricRegistry())
.convertRatesTo(TimeUnit.SECONDS) // 輸出比率的時間單位
.convertDurationsTo(TimeUnit.MILLISECONDS) // 輸出頻率的時間單位
.build();
reporter.start(10, TimeUnit.SECONDS); // 定義每10秒執行一次輸出
return reporter;
生產環境上的此類信息一般會入日誌系統,Metrics提供將監控輸出到日誌的方式。一下時輸出到日誌的配置:
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);