前言
在复杂的分布式系统中,为了更好地了解系统的运行情况,往往我们会有多样的metric性能指标来帮助我们了解这里面的情况。在这其中的metric指标按照功能,还可以再分成多种不同类的指标类型,例如统计自增长类型的数据,或者统计均值速率型的等等。一个设计优良的metric收集模型无疑对于一个系统来说是十分重要的,本文我们来聊聊metric的框架设计的相关内容,对于一个系统而言,我们如何去设计一个简单又实用的metric框架模型。
Metric指标类型
如前面所提到的,不同的metric指标适用于不同的应用场景,因此我们需要了解一些常用的metric指标类型以及它的含义:
- Counter,单调自增型统计数据,从系统启动的那一刻起,就开始进行技术统计了。
- Gauge,实时测量值类型,这个指标获取的系统指标的当前实时值。
- Average,区间时间段内的平均值。
- Histogram,过去一定时间段内,指标数据的直方图分布情况,这个有点统计学的味道在里面了。
现在问题来了,对于上述四种情况,我们分别适用哪些场景呢?下面我们一个一个来看。
首先对于Counter纯计数类型的metric,因为它是单调递增型的,所以比较适用于那些同样具有单调递增属性的指标,比如transaction数,或者一些error发生次数的累计等等。
然后是Gauge类型的,此类型指标适用于中间状态存储的数据结构,因为它会有内部存储状态的实时变化,所以我们需要去了解其中的实时状态。具体一点的比如说,某个消息Queue的size指标,或者Cache的当前size等等。总而言之,此指标适用于状态瞬息万变的指标数据。
对于后两种的指标统计,它们偏重于单位时段内的数据分析,比较适用于系统内的RPC请求耗时调用统计。
设计一个优良的metric框架,了解Metric指标类型只是一个小小的背景前提。
Metric框架的设计
对于Metric框架设计而言,我们需要考虑的问题就要更多一些了,比如以下必须要解决的问题:
- 我需要用哪些类型的Metric指标,收集哪些指标数据?
- 应用层如何通过Metric框架来收集我们想要的metric指标?
- 收集好后的Metric指标如何进行指标的expose?JMX,Console?
熟悉metric指标统计代码逻辑的同学,应该经常会看到metric registry的对象。在此节Metric框架模型的设计里,我们也会有Metric Registry对象,不过这里是一个大的集合类,MetricRegistries,在此集合汇总类里包含了各个小的metric注册类。因此这里首先会衍生出两个概念:
- Metric Registry:此对象可针对各个不同服务级别层次,构造独立的metric registry,进而构造出具体的metric收集指标,进行指标的统计。
- Metric Registry集合:此集合类是Metric Registry的管理类,管理了所有已经注册的Metric Registry对象,外部可以通过向此类查询得到之前创建的Metric Registry类。同样的Metric Registry对象只会维持一份,不过会有额外的引用计数属性。在实际场景内,会存在多个调用方获取Metric Registry进行metric收集。只有当Registry引用归为0后,此对象才会被移除。
在实际的使用场景中,这些Metric Registry对象首先将被初始化在具体使用类中,如下代码所示:
this.metricRegistry = LogServiceMetricsRegistry
.createMetricRegistryForLogService(groupId.toString(), server.getId().toString());
然后通过metricRegistry进行metric变量的获取,如下timer指标即上面提到的Histogram含义的指标
this.readNextQueryTimer = metricRegistry.timer("readNextQueryTime");
this.startIndexTimer= metricRegistry.timer("startIndexTime");
this.sizeRequestTimer = metricRegistry.timer("sizeRequestTime");
this.getStateTimer = metricRegistry.timer("getStateTime");
this.lastIndexQueryTimer = metricRegistry.timer("lastIndexQueryTime");
this.lengthQueryTimer = metricRegistry.timer("lengthQueryTime");
this.syncRequesTimer = metricRegistry.timer("syncRequesTime");
this.appendRequestTimer = metricRegistry.timer("appendRequestTime");
this.getCloseLogTimer = metricRegistry.timer("getCloseLogTime");
//archiving request time not the actual archiving time
this.archiveLogRequestTimer = metricRegistry.timer("archiveLogRequestTime");
this.archiveLogTimer = metricRegistry.timer("archiveLogTime");
最后在实际调用处,我们会进行metric指标的统计,
@Override
public CompletableFuture<Message> applyTransaction(TransactionContext trx) {
try {
checkInitialization();
final LogEntryProto entry = trx.getLogEntry();
LogServiceRequestProto logServiceRequestProto =
LogServiceRequestProto.parseFrom(entry.getStateMachineLogEntry().getLogData());
switch (logServiceRequestProto.getRequestCase()) {
case CHANGESTATE:
return recordTime(getCloseLogTimer, new Task(){
@Override public CompletableFuture<Message> run() {
return processChangeState(logServiceRequestProto);
}});
case APPENDREQUEST:
return recordTime(appendRequestTimer, new Task(){
@Override public CompletableFuture<Message> run() {
return processAppendRequest(trx, logServiceRequestProto);
}});
case SYNCREQUEST:
return recordTime(syncRequesTimer, new Task(){
@Override public CompletableFuture<Message> run() {
return processSyncRequest(trx, logServiceRequestProto);
}});
case ARCHIVELOG:
return updateArchiveLogInfo(logServiceRequestProto);
default:
//TODO
return null;
}
} catch (IOException e) {
// TODO exception handling
throw new RuntimeException(e);
}
}
...
// record time操作方法
protected CompletableFuture<Message> recordTime(Timer timer, Task task) {
final Timer.Context timerContext = timer.time();
try {
return task.run();
} finally {
timerContext.stop();
}
}
通过前面的过程,metric指标的收集过程全部完成了,但是这里还缺少重要的metric指标展示的部分,metric指标只有通过向外部暴露才能发挥其独有的作用。因此在metric框架中,额外需要metric report的功能模块,外部展示的方式可以是常用的JMX方式或者普通的Console终端展示模式。以下是简单的Console模式的metric输出report,
9/12/19 11:27:37 PM ============================================================
-- Gauges ----------------------------------------------------------------------
com.learn.gauge.freeMemory
value = 234591968
9/12/19 11:27:38 PM ============================================================
-- Gauges ----------------------------------------------------------------------
com.learn.gauge.freeMemory
value = 234591968
9/12/19 11:27:39 PM ============================================================
-- Gauges ----------------------------------------------------------------------
com.learn.gauge.freeMemory
value = 234591968
9/12/19 11:27:40 PM ============================================================
-- Gauges ----------------------------------------------------------------------
com.learn.gauge.freeMemory
value = 234591968
按照此小节的metric框架模型阐述,此结构图如下所示:
总而言之,Metric指标统计在复杂分布式系统中正扮演着越来越重要的角色。