基於dropwizard/metrics ,kafka,zabbix構建應用統計資料收集展示系統

五柳-先生發表於2016-01-26

想要實現的功能

  • 應用可以用少量的程式碼,實現統計某類資料的功能
  • 統計的資料可以很方便地展示

metrics

metrics,按字面意思是度量,指標。

舉具體的例子來說,一個web伺服器:

  • 一分鐘內請求多少次?
  • 平均請求耗時多長?
  • 最長請求時間?
  • 某個方法的被呼叫次數,時長?

以快取為例:

  • 平均查詢快取時間?
  • 快取獲取不命中的次數/比例?

以jvm為例:

  • GC的次數?
  • Old Space的大小?

在一個應用裡,需要收集的metrics資料是多種多樣的,需求也是各不同的。需要一個統一的metrics收集,統計,展示平臺。

流行的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的。

metrics的種類

dropwizard/metrics 裡主要把metrics分為下面幾大類:

https://dropwizard.github.io/metrics/3.1.0/getting-started/

Gauges

gauge用於測量一個數值。比如佇列的長度:

1
2
3
4
5
6
7
8
9
10
11
12
13
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();
}
});
}
}

Counters

counter是AtomicLong型別的gauge。比如可以統計阻塞在佇列裡的job的數量:

1
2
3
4
5
6
7
8
9
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();
}

Histograms

histogram統計資料的分佈。比如最小值,最大值,中間值,還有中位數,75百分位, 90百分位, 95百分位, 98百分位, 99百分位, and 99.9百分位的值(percentiles)。

比如request的大小的分佈:

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);
}

Timers

timer正如其名,統計的是某部分程式碼/呼叫的執行時間。比如統計response的耗時:

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();
}
}

Health Checks

這個實際上不是統計資料。是介面讓使用者可以自己判斷系統的健康狀態。如判斷資料庫是否連線正常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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());
}
}
}

Metrics Annotation

利用dropwizard/metrics 裡的annotation,可以很簡單的實現統計某個方法,某個值的資料。
如:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 統計呼叫的次數和時間
*/

@Timed
public void call() {
}

/**
* 統計登陸的次數
*/

@Counted
public void userLogin(){
}

想要詳細瞭解各種metrics的實際效果,簡單的執行下測試程式碼,用ConsoleReporter輸出就可以知道了。

metrics資料的傳輸和展示

dropwizard/metrics 裡提供了reporter的介面,使用者可以自己實現如何處理metrics資料。

dropwizard/metrics有不少現成的reporter:

1
ConsoleReporter  輸出到stdout
JmxReporter  轉化為MBean
metrics-servlets  提供http介面,可以查詢到metrics資訊
CsvReporter 輸出為CSV檔案
Slf4jReporter 以log方式輸出
GangliaReporter  上報到Ganglia
GraphiteReporter 上報到Graphite

上面的各種reporter中,Ganglia開源多年,但缺少一些監控的功能,圖形展示也很簡陋。Graphite已經停止開發了。

而公司所用的監控系統是zabbix,而dropwizard/metrics沒有現成的zabbix reporter。

zabbix的限制

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都是相對固定的,那麼就可以通過模板的方式,比較方便地統一建立item, graph了。

另外想要實現自動建立item,比較好的辦法是通過zabbix api了。

但目前Java版沒有實現,於是實現了一個簡單的:

https://github.com/hengyunabc/zabbix-api

dropwizard/metrics zabbix reporter

基於上面的template的思路,實現了一個dropwizard/metrics 的zabbix reporter。

原理是,通過zabbix sender,把metrics資料直接傳送到zabbix server上。

https://github.com/hengyunabc/zabbix-sender

https://github.com/hengyunabc/metrics-zabbix

dropwizard/metrics傳送到kafka,再從kafka發到zabbix

上面的方案感覺還是不太理想:

  • 沒有實現自動化,還要手動為每一個應用配置template,不夠靈活
  • 所有的資料都傳送到一個zabbix server上,擔心效能有瓶頸
    於是,新的思路是,把metrics資料傳送到kafka上,然後再從kafka上消費,再把資料傳到zabbix server上。

這樣的好處是:

  • kafka可以靈活擴容,不會有效能瓶頸
  • 從kafka上消費metrics資料,可以靈活地用zabbix api來建立item, graph

於是實現了兩個新專案:

Java程式先把metrics資料上報到kafka,然後kafka consumer從metrics資料裡,提取出host, key資訊,再用zabbix-api在zabbix server上建立item,最後把metrics資料上報給zabbix server。

自動建立的zabbix item的效果圖:
zabbix-api-create-zabbix-itemzabbix-api-create-zabbix-item

在zabbix上顯示的使用者自定義的統計資料的圖:
zabbix-test-response-sizezabbix-test-response-size

資料的聚合

比如,統計介面的訪問次數,而這個介面部署在多臺伺服器上,那麼如何展示聚合的資料?

zabbix自帶有聚合功能,參考:

http://opsnotes.net/2014/10/24/zabbix_juhe/ 實戰:Zabbix 聚合功能配置與應用

metrics的實現的探討

從dropwizard/metrics裡,我們可以看到一種簡單直觀的實現:

  • app內收集統計資料,計算好具體的key/value
  • 定時上報

另外,用分散式呼叫追蹤(dapper/zipkin)的辦法,也可以實現部分metrics的功能。
比如某個方法的呼叫次數,快取命中次數等。

當然,兩者只是部分功能有重合。

dropwizard/metrics 是一種輕量級的手段,使用者可以隨意增加自己想要的統計資料,程式碼也很靈活。有些簡單直觀的統計資料如果用分散式呼叫追蹤的方式來做,顯然會比較吃力,得不償失。

總結

本文提出並實現了,利用dropwizard/metrics做資料統計,kafka做資料傳輸,zabbix做資料展示的完整流程。

對於開發者來說,不需要關心具體的實現,只需要按dropwizard/metrics的文件做統計,再配置上metrics-kafka reporter即可。

轉載:http://hengyunabc.github.io/about-metrics/

相關文章