新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便可。