zoukankan      html  css  js  c++  java
  • 论分布式系统中Metric框架的设计

    前言


    在复杂的分布式系统中,为了更好地了解系统的运行情况,往往我们会有多样的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指标统计在复杂分布式系统中正扮演着越来越重要的角色。

  • 相关阅读:
    运算放大器和比较器的区别
    求和电路
    差分放大电路
    含T型网络的反相放大器
    三运放仪用放大器
    cmd window关闭端口程序
    List及其实现类
    docker-compose安装elasticsearch集群+kibana
    sp_spaceused 查询表或者库的大小
    转载:Windows下利用bat批量打开程序
  • 原文地址:https://www.cnblogs.com/bianqi/p/12183513.html
Copyright © 2011-2022 走看看