【Hive】Hive Metrics体系

Hive常用的长服务主要有HiveServer2和MetaStore,这两者都可以配置一些监控数据。HiveServer2可以配置若干监控,有关HiveServer2的更多介绍可以查看文档Setting Up Hiveser2

一、HiveServer2 UI

从Hive 2.0.0版本以后,提供了一个Hiveser2的UI界面,默认通过10002端口访问。在该页面上可以看到当前活跃的HiveServe2连接Sessions,以及日志,Metrics信息等。

http://host:10002/jmx 路径下可以获取到HiveServer2一些监控项,但主要是jvm相关的监控,通过参数配置可以增加其他监控项。Hive中可配置的Metrics相关信息可以参考Hive Metrics,大致列举如下,

参数默认值含义
hive.metastore.metrics.enabledfalse打开metastore的监控项
hive.server2.metrics.enabledfalse打开hiveserver2的监控项
hive.service.metrics.classorg.apache.hadoop.hive.common.metrics.metrics2.CodahaleMetricshive监控体系实现类
hive.service.metrics.reporterJSON_FILE, JMX监控信息的返回格式,可选JSON_FILE, CONSOLE, JMX多个选项用逗号分隔
hive.service.metrics.file.frequency5秒监控项更新时间间隔
hive.service.metrics.file.location/tmp/report.json当使用metrics2的默认类,并且返回JSON_FILE格式时,监控信息会输出到该文件中,文件内容会覆盖,时间间隔由参数控制

将参数hive.metastore.metrics.enabled打开后,再次访问jmx路径会看到多了很多metrics:name=开头的监控项,

hive的Metrics信息如下所示,

Metrics示例

二、Hive源代码

如上图所示,可以看到有metrics:name=api_以及metrics:name=active_calls_开头的监控项明细信息,这些参数都在org.apache.hadoop.hive.common.metrics.metrics2.CodahaleMetrics类中定义,正如上面提到的,这个类在Hive-2版本中是参数hive.service.metrics.class的默认值。

1、MetricsFactory类

CodahaleMetrics类是在org.apache.hadoop.hive.common.metrics.common.MetricsFactory类中通过init方法构造出来的,过程如下所示,通过参数来选择具体的实现类。

  public synchronized static void init(HiveConf conf) throws Exception {
    if (metrics == null) {
      Class metricsClass = conf.getClassByName(
        conf.getVar(HiveConf.ConfVars.HIVE_METRICS_CLASS));
      Constructor constructor = metricsClass.getConstructor(HiveConf.class);
      metrics = (Metrics) constructor.newInstance(conf);
    }
  }

JvmPauseMonitor, HiveMetastore, PerfLogger, Operation, SQLOperation, HiveServer2等类通过调用MetricsFactory.getInstance方法获取Metrics对象。

1、Metrics收集

上面提到主要有5个类都会通过Metrics对象收集监控数据,接下来逐个分析。

(1)org.apache.hadoop.hive.common.JvmPauseMonitor

从类注释看,该类是基于Hadoop中的JvmPauseMonitor类实现的。当Hive启动JVM进程时会生成JvmPauserMonitor对象来收集JVM相关的监控数据,比较常见的是HiveServer2HiveMetaStore进程。使用方法如下,

          JvmPauseMonitor pauseMonitor = new JvmPauseMonitor(conf);
          pauseMonitor.start();

接下来看一下start方法,在该方法中会生成一个守护线程,MonitorJvmPauserMonitor内部类。

  public void start() {
    Preconditions.checkState(monitorThread == null,
      "JvmPauseMonitor thread is Already started");
    monitorThread = new Daemon(new Monitor());
    monitorThread.start();
  }

Monitor类中,三次调用incrementMetricsCounter方法调用Metrics.incrementCounter方法来累加指定监控项的数值,

    private void incrementMetricsCounter(String name, long count) {
      Metrics metrics = MetricsFactory.getInstance();
      if (metrics != null) {
        try {
          metrics.incrementCounter(name, count);
        } catch (Exception e) {
          LOG.warn("Error Reporting JvmPauseMonitor to Metrics system", e);
        }
      }
    }

这三次调用的监控项定义在MetricsConstant中,

  public static final String JVM_PAUSE_INFO = "jvm.pause.info-threshold";
  public static final String JVM_PAUSE_WARN = "jvm.pause.warn-threshold";
  public static final String JVM_EXTRA_SLEEP = "jvm.pause.extraSleepTime";

(2)HiveMetaStore

这个类中最主要的方法是startFunction,如下所示,

   private String startFunction(String function, String extraLogInfo) {
      incrementCounter(function);
      logInfo((getThreadLocalIpAddress() == null ? "" : "source:" + getThreadLocalIpAddress() + " ") +
          function + extraLogInfo);
      if (MetricsFactory.getInstance() != null) {
        try {
          MetricsFactory.getInstance().startStoredScope(function);
        } catch (IOException e) {
          LOG.debug("Exception when starting metrics scope"
            + e.getClass().getName() + " " + e.getMessage(), e);
        }
      }
      return function;
    }

调用该方法的入口大致如下,每一次对元数据的操作,比如建表,建库,查表等都有对应的处理函数,同时会通过该方法更新一下Metrics信息。

startFunction方法调用

所有通过CodahaleMetrics.startStoredScope方法进行监控的指标,都会在方法名前加一个“api_”前缀。下面这段代码位于CodahaleMetrics中。

  public static final String API_PREFIX = "api_";

  public void startStoredScope(String name) throws IOException {
    name = API_PREFIX + name;
    if (threadLocalScopes.get().containsKey(name)) {
      threadLocalScopes.get().get(name).open();
    } else {
      threadLocalScopes.get().put(name, new CodahaleMetricsScope(name, this));
    }
  }

(3)PerfLogger

PerfLogger中,主要在beginMetrics方法中用到了Metrics,而beginMetrics方法的入口又是PerfLogBegin

  public void PerfLogBegin(String callerName, String method) {
    long startTime = System.currentTimeMillis();
    startTimes.put(method, new Long(startTime));
    if (LOG.isDebugEnabled()) {
      LOG.debug("<PERFLOG method=" + method + " from=" + callerName + ">");
    }
    beginMetrics(method);
  }

PerfLogger是一个辅助类,可以用于监控代码片段的执行效率,如上面的代码所示,当日志调整到DEBUG级别时,调用PerfLogger相关方法将会打印方法的耗时信息等,在PerfLogEnd方法中,将会输出一个方法完整的运行耗时等性能信息。当Hive需要排查性能问题时,可以使用本方法。

Hive中有多个类使用了PerfLogger,比如org.apache.hadoop.hive.ql.Driver,在compile方法中的调用如下所示,

      perfLogger.PerfLogBegin(CLASS_NAME, PerfLogger.PARSE);
      ParseDriver pd = new ParseDriver();
      ASTNode tree = pd.parse(command, ctx);
      tree = ParseUtils.findRootNonNullToken(tree);
      perfLogger.PerfLogEnd(CLASS_NAME, PerfLogger.PARSE);

在解析之前,调用PerfLogBegin方法,传入类名,以及对应的parse标识符,在这段逻辑结束时,调用PerfLogEnd方法,传入相同参数,日志中将会显示这一段逻辑从开始到结束的执行时长等信息。

(4)HiveServer2

在HiveServer2中,直接操作Metrics的地方只有两处,一处是在init方法中,如下所示,当参数hive.server2.metrics.enabled打开时,初始化MetricsFactory生成对应的Metrics类。

  public synchronized void init(HiveConf hiveConf) {
    //Initialize metrics first, as some metrics are for initialization stuff.
    try {
      if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_METRICS_ENABLED)) {
        MetricsFactory.init(hiveConf);
      }
    } catch (Throwable t) {
      LOG.warn("Could not initiate the HiveServer2 Metrics system.  Metrics may not be reported.", t);
    }
	
	cliService = new CLIService(this);
    addService(cliService);
	...
}

另一处是在stop方法中,停止HiveServer2服务时,调用MetricsFactory.close()方法关闭Metrics。

上面代码中,启动HiveServer2后,会生成CLIService对象,后续连接该Server的提交的SQL任务,具体的Metrics信息都由下面的Operation处理。

(4)Operation及其子类

Operation有两个直接子类MetadataOperationExecuteStagementOperation,子类继承关系如下图所示,

Operation及其子类

2、CodahaleMetrics

该类继承自org.apache.hadoop.hive.common.metrics.common.Metrics类,

(1)incrementCounter(String name, long increment)

该方法在前面的代码中已经提到,根据监控项的名称name,找到对应监控项的值,然后增加该监控项的数值increment。

(2)decrementCounter(String name, long decrement)

与上面的方法功能相反,减少监控项的数值。

(2)startStoredScope

前面HiveMetaStore.startFunction方法,连接该metastore执行不同操作时调用的方法,全部进入这个方法中,该方法的监控值加1,然后将数据存入StoredScope对象中,这个对象中存储的数值可以由JMXReporter或者JsonFileReporter向外部暴露。

    private String startFunction(String function, String extraLogInfo) {
      incrementCounter(function);
      logInfo((getThreadLocalIpAddress() == null ? "" : "source:" + getThreadLocalIpAddress() + " ") +
          function + extraLogInfo);
      if (MetricsFactory.getInstance() != null) {
        try {
          MetricsFactory.getInstance().startStoredScope(function);
        } catch (IOException e) {
          LOG.debug("Exception when starting metrics scope"
            + e.getClass().getName() + " " + e.getMessage(), e);
        }
      }
      return function;
    }

(3)endStoredScope

从HiveMetaStore中开始过程与StartStoredScope相反。

从当前线程中移除指定监控项。

  public void endStoredScope(String name) throws IOException {
    name = API_PREFIX + name;
    if (threadLocalScopes.get().containsKey(name)) {
      threadLocalScopes.get().get(name).close();
      threadLocalScopes.get().remove(name);
    }
  }

(5)addGauge

将监控项加入到监控存储对象metricRegistry中,该对象是MetricRegistry类型,后面有提到。

  public void addGauge(String name, final MetricsVariable variable) {
    Gauge gauge = new Gauge() {
      @Override
      public Object getValue() {
        return variable.getValue();
      }
    };
    try {
      gaugesLock.lock();
      gauges.put(name, gauge);
      // Metrics throws an Exception if we don't do this when the key already exists
      if (metricRegistry.getGauges().containsKey(name)) {
        LOGGER.warn("A Gauge with name [" + name + "] already exists. "
          + " The old gauge will be overwritten, but this is not recommended");
        metricRegistry.remove(name);
      }
      metricRegistry.register(name, gauge);
    } finally {
      gaugesLock.unlock();
    }
  }

(6)initReporting

初始化Reporter对象,由参数hive.service.metrics.reporter控制,常用的是JMX, JSON_FILE。

3、Reporter

这里是向外部暴露Metrics信息的地方,常用的有以下两个实现,由参数hive.service.metrics.reporter配置具体的输出方式。在上面的initReporting来构造对应的Reporter。

(1)JMXReporter

略,位于metrics-core-3.1.0.jarcom.codahale.metrics路径下。构造好该对象,调用start方法。

(2)JsonFileReporter

JsonFileReporterCodahaleMetrics的内部类。start方法定时从MetricRegistry类中取出收集到的监控数据,输出到hive.service.metrics.file.location路径下。

上面的Gauge对象也通过metricRegistry.register(name, gauge)方法注册进来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值