新blog地址:http://hengyunabc.github.io/about-metrics/
metrics,按字面意思是度量,指標。
舉具體的例子來講,1個web服務器:
- 1分鐘內要求多少次?
- 平均要求耗時多長?
- 最長要求時間?
- 某個方法的被調用次數,時長?
以緩存為例:
- 平均查詢緩存時間?
- 緩存獲得不命中的次數/比例?
以jvm為例:
- GC的次數?
- Old Space的大小?
在1個利用里,需要搜集的metrics數據是多種多樣的,需求也是各不同的。需要1個統1的metrics搜集,統計,展現平臺。
https://github.com/dropwizard/metrics
java實現,很多開源項目用到,比如hadoop,kafka。下面稱為dropwizard/metrics。
https://github.com/tumblr/colossus
scala實現,把數據存到OpenTsdb上。
spring boot 項目里的metrics:
http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html
spring boot里的metrics很多都是參考dropwizard/metrics的。
dropwizard/metrics 里主要把metrics分為下面幾大類:
https://dropwizard.github.io/metrics/3.1.0/getting-started/
gauge用于丈量1個數值。比如隊列的長度:
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();
}
});
}
}
counter是AtomicLong類型的gauge。比如可以統計阻塞在隊列里的job的數量:
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();
}
histogram統計數據的散布。比如最小值,最大值,中間值,還有中位數,75百分位, 90百分位, 95百分位, 98百分位, 99百分位, and 99.9百分位的值(percentiles)。
比如request的大小的散布:
private final Histogram responseSizes = metrics.histogram(name(RequestHandler.class, "response-sizes"));
public void handleRequest(Request request, Response response) {
// etc
responseSizes.update(response.getContent().length);
}
timer正如其名,統計的是某部份代碼/調用的運行時間。比如統計response的耗時:
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();
}
}
這個實際上不是統計數據。是接口讓用戶可以自己判斷系統的健康狀態。如判斷數據庫是不是連接正常:
final HealthCheckRegistry healthChecks = new HealthCheckRegistry();
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());
}
}
}
利用dropwizard/metrics 里的annotation,可以很簡單的實現統計某個方法,某個值的數據。
如:
/**
* 統計調用的次數和時間
*/
@Timed
public void call() {
}
/**
* 統計登陸的次數
*/
@Counted
public void userLogin(){
}
想要詳細了解各種metrics的實際效果,簡單的運行下測試代碼,用ConsoleReporter輸出就能夠知道了。
dropwizard/metrics 里提供了reporter的接口,用戶可以自己實現如何處理metrics數據。
dropwizard/metrics有很多現成的reporter:
ConsoleReporter 輸出到stdout
JmxReporter 轉化為MBean
metrics-servlets 提供http接口,可以查詢到metrics信息
CsvReporter 輸出為CSV文件
Slf4jReporter 以log方式輸出
GangliaReporter 上報到Ganglia
GraphiteReporter 上報到Graphite
上面的各種reporter中,Ganglia開源多年,但缺少1些監控的功能,圖形展現也很簡陋。Graphite已停止開發了。
而公司所用的監控系統是zabbix,而dropwizard/metrics沒有現成的zabbix reporter。
zabbix上報數據通經常使用zabbix agent或zabbix trapper。
用戶自己上報的數據通經常使用zabbix trapper來上報。
zabbix上搜集數據的叫item,每一個item都有自己的key,而這些item不會自動創建。zabbix有Low-level discovery,可以自動創建item,但是也相當麻煩,而且key的命名非常奇怪。不如直接用template了。
https://www.zabbix.com/documentation/2.4/manual/discovery/low_level_discovery
假定zabbix上不同的利用的key都是相對固定的,那末就能夠通過模板的方式,比較方便地統1創建item, graph了。
另外想要實現自動創建item,比較好的辦法是通過zabbix api了。
但目前Java版沒有實現,因而實現了1個簡單的:
https://github.com/hengyunabc/zabbix-api
基于上面的template的思路,實現了1個dropwizard/metrics 的zabbix reporter。
原理是,通過zabbix sender,把metrics數據直接發送到zabbix server上。
https://github.com/hengyunabc/zabbix-sender
https://github.com/hengyunabc/metrics-zabbix
上面的方案感覺還是不太理想:
- 沒有實現自動化,還要手動為每個利用配置template,不夠靈活
- 所有的數據都發送到1個zabbix server上,擔心性能有瓶頸
因而,新的思路是,把metrics數據發送到kafka上,然后再從kafka上消費,再把數據傳到zabbix server上。
這樣的好處是:
- kafka可以靈活擴容,不會有性能瓶頸
- 從kafka上消費metrics數據,可以靈活地用zabbix api來創建item, graph
因而實現了兩個新項目:
- https://github.com/hengyunabc/metrics-kafka
- https://github.com/hengyunabc/kafka-zabbix
Java程序先把metrics數據上報到kafka,然后kafka consumer從metrics數據里,提取出host, key信息,再用zabbix-api在zabbix server上創建item,最后把metrics數據上報給zabbix server。
自動創建的zabbix item的效果圖:
在zabbix上顯示的用戶自定義的統計數據的圖:
比如,統計接口的訪問次數,而這個接口部署在多臺服務器上,那末如何展現聚合的數據?
zabbix自帶有聚合功能,參考:
http://opsnotes.net/2014/10/24/zabbix_juhe/ 實戰:Zabbix 聚合功能配置與利用
從dropwizard/metrics里,我們可以看到1種簡單直觀的實現:
- app內搜集統計數據,計算好具體的key/value
- 定時上報
另外,用散布式調用追蹤(dapper/zipkin)的辦法,也能夠實現部份metrics的功能。
比如某個方法的調用次數,緩存命中次數等。
固然,二者只是部份功能有重合。
dropwizard/metrics 是1種輕量級的手段,用戶可以隨便增加自己想要的統計數據,代碼也很靈活。有些簡單直觀的統計數據如果用散布式調用追蹤的方式來做,明顯會比較費勁,得不償失。
本文提出并實現了,利用dropwizard/metrics做數據統計,kafka做數據傳輸,zabbix做數據展現的完全流程。
對開發者來講,不需要關心具體的實現,只需要按dropwizard/metrics的文檔做統計,再配置上metrics-kafka reporter便可。