【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.enabled false 打開metastore的監控項
hive.server2.metrics.enabled false 打開hiveserver2的監控項
hive.service.metrics.class org.apache.hadoop.hive.common.metrics.metrics2.CodahaleMetrics hive監控體系實現類
hive.service.metrics.reporter JSON_FILE, JMX 監控信息的返回格式,可選JSON_FILE, CONSOLE, JMX多個選項用逗號分隔
hive.service.metrics.file.frequency 5秒 監控項更新時間間隔
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)方法註冊進來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章