天天看點

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

新blog位址: http://hengyunabc.github.io/about-metrics/

想要實作的功能

  • 應用可以用少量的代碼,實作統計某類資料的功能
  • 統計的資料可以很友善地展示

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用于測量一個數值。比如隊列的長度:

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的數量:

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的大小的分布:

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的耗時:

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

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

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,可以很簡單的實作統計某個方法,某個值的資料。

如:

/**
     * 統計調用的次數和時間
     */
    @Timed
    public void call() {
    }

    /**
     * 統計登陸的次數
     */
    @Counted
    public void userLogin(){
    }           

想要詳細了解各種metrics的實際效果,簡單的運作下測試代碼,用ConsoleReporter輸出就可以知道了。

metrics資料的傳輸和展示

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開源多年,但缺少一些監控的功能,圖形展示也很簡陋。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

于是實作了兩個新項目:

-

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的效果圖:

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

在zabbix上顯示的使用者自定義的統計資料的圖:

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

資料的聚合

比如,統計接口的通路次數,而這個接口部署在多台伺服器上,那麼如何展示聚合的資料?

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即可。