Hive相关的两个疑问[一]

  从疑问出发,寻根问底探究原理:
1. Hive CLI/Hive Beeline/Hive JDBC断开连接之后,运行在Yarn上的任务是否继续运行?
2. 我们知道JDBC是无法做缓存的,那么HUE是如何实现缓存功能的?

  本部分研究第一个问题,即客户端的状态是否影响Yarn上正在运行的任务。

  首先我们从现象谈起然后逐渐找到问题的答案,既是一个解疑的过程也是一个探究研究问题思路的过程,更是一个增长知识的过程。

1. 现象描述

  在Hadoop生态中执行Hive任务时,经常会出现各种各样的问题。今天发现了一个特别有意思的事情,执行调度的同学说他们执行的任务,在beeline调用端任务失败,但业务同学去数据库查看相应的数据记录,初步发现没有异常同步成功。这就引起了一个问题:当beeline异常断开时,当前运行的yarn的任务是否同步退出?还是继续执行完成?

  根据这个问题,我首先做了现象的重现,结果发现:断开连接之后,执行在yarn上的任务会同步结束。分别通过HIVE CLI、Beeline、JDBC三种方式做得测试,结果都表明断开连接之后,任务无法接续运行。除了一种情况那就是HIVE CLI模式下,直接通过KILL命令杀死Hive进程,Hive客户端的JVM直接退出,但正在Yarn上运行的任务未退出继续运行,直到结束。

2. Hive三种方式访问Hive路径

Hive的三种访问方式
其中Hive Beeline使用的也是Thrift API访问方式。

3. 寻根问底

3.1. [Hive CLI]方式

  1. org.apache.hadoop.hive.cli.CliDriver
  2. ctrl+c方式断开:
    CliDriver注册了信号处理:
private boolean interruptRequested;

@Override
public void handle(Signal signal) {
  boolean initialRequest = !interruptRequested;
  interruptRequested = true;

  // Kill the VM on second ctrl+c
  if (!initialRequest) {
    console.printInfo("Exiting the JVM");
    System.exit(127);
  }

  // Interrupt the CLI thread to stop the current statement and return to prompt
  console.printInfo("Interrupting... Be patient, this might take some time.");
  console.printInfo("Press Ctrl+C again to kill JVM");

  // First, kill any running MR jobs
  HadoopJobExecHelper.killRunningJobs();
  TezJobExecHelper.killRunningJobs();
  HiveInterruptUtils.interrupt();
}

  通过以上的源码可以看出如果我们使用ctrl+c命令,那么触发Kill操作,依次处理了 HadoopJob、TezJob以及Hive的退出操作;如果前后两次使用ctrl+c命令,那么直接Kill JVM。
3. 直接Kill -9方式退出Hive:针对Yarn MR任务模式,对于LocalMRJob不做讨论。
通过阅读源码:

} else if (cmd_trimmed.startsWith("!")) {

String shell_cmd = cmd_trimmed.substring(1);
shell_cmd = new VariableSubstitution(new HiveVariableSource() {
  @Override
  public Map<String, String> getHiveVariable() {
    return SessionState.get().getHiveVariables();
  }
}).substitute(ss.getConf(), shell_cmd);

// shell_cmd = "/bin/bash -c \'" + shell_cmd + "\'";
try {
  ShellCmdExecutor executor = new ShellCmdExecutor(shell_cmd, ss.out, ss.err);
  ret = executor.execute();
  if (ret != 0) {
    console.printError("Command failed with exit code = " + ret);
  }
} catch (Exception e) {
  console.printError("Exception raised from Shell command " + e.getLocalizedMessage(),
    stringifyException(e));
  ret = 1;
}
 }  else { // local mode

可以看出CliDriver对于命令的处理是通过调用外部线程进行的。如果直接结束JVM,那么外部线程没有影响继续运行,所以我们看到的表象就是人物继续运行知道运行结束。

3.2. Beeline:thrift server方式访问Hive

  1. 访问主类:Beeline: org.apache.hive.beeline.BeeLine
  2. 通过ctrl+c方式断开:通过ctrl+c方式断开连接,Hive会捕捉到相应的信号,从而进行相关处理。信号处理的handler定义如下
  private Statement stmt = null;

  SunSignalHandler () {
    // Interpret Ctrl+C as a request to cancel the currently
    // executing query.
    Signal.handle (new Signal ("INT"), this);
  }

  public void setStatement(Statement stmt) {
    this.stmt = stmt;
  }

  public void handle (Signal signal) {
    try {
      if (stmt != null) {
        stmt.cancel();
      }
    } catch (SQLException ex) {
      // ignore
    }
  }

  从处理的代码中可以看出,如果出现ctrl+c信号,Hive会取消目前相关操作。
3. 异常断开:包括超时、宕机等异常情况
如果是Thrift RPC的连接断开,那么从源码中可以看出

HIVE_SERVER2_CLOSE_SESSION_ON_DISCONNECT("hive.server2.close.session.on.disconnect", true,
      "Session will be closed when connection is closed. Set this to false to have session outlive its parent connection.")

如果connection断开,那么session会立马被关闭。而HiveServer2关闭session的操作,如下所示

ThriftCLIServerContext context = (ThriftCLIServerContext) serverContext;
SessionHandle sessionHandle = context.getSessionHandle();
if (sessionHandle != null) {
  LOG.info("Session disconnected without closing properly. ");
  try {
    boolean close = cliService.getSessionManager().getSession(sessionHandle).getHiveConf()
      .getBoolVar(ConfVars.HIVE_SERVER2_CLOSE_SESSION_ON_DISCONNECT);
    LOG.info((close ? "" : "Not ") + "Closing the session: " + sessionHandle);
    if (close) {
      cliService.closeSession(sessionHandle);
    }
  } catch (HiveSQLException e) {
    LOG.warn("Failed to close session: " + e, e);
  }
}

此处cliService的关闭最终会导致HiveSessionImpl的关闭操作,其中关闭路径为:CLIService.closeSession–>SessionManager.closeSession–> HiveSession.close

  try {
      acquire(true, false);
      // Iterate through the opHandles and close their operations
      List<OperationHandle> ops = null;
      synchronized (opHandleSet) {
        ops = new ArrayList<>(opHandleSet);
        opHandleSet.clear();
      }
      for (OperationHandle opHandle : ops) {
        operationManager.closeOperation(opHandle);
      }
      // Cleanup session log directory.
      cleanupSessionLogDir();
      HiveHistory hiveHist = sessionState.getHiveHistory();
      if (null != hiveHist) {
        hiveHist.closeStream();
      }
      try {
        sessionState.resetThreadName();
        sessionState.close();
      } finally {
        sessionState = null;
      }
    } catch (IOException ioe) {
      throw new HiveSQLException("Failure to close", ioe);
    } finally {
      if (sessionState != null) {
        try {
          sessionState.resetThreadName();
          sessionState.close();
        } catch (Throwable t) {
          LOG.warn("Error closing session", t);
        }
        sessionState = null;
      }
      if (sessionHive != null) {
        try {
          Hive.closeCurrent();
        } catch (Throwable t) {
          LOG.warn("Error closing sessionHive", t);
        }
        sessionHive = null;
      }
      release(true, false);
  }

从这里可以看到Session通过迭代将所有的操作逐渐关闭,并清除Session相关日志、关闭任务历史日志流。

3.3. [Hive JDBC]:thrift server方式访问Hive

  1. 访问主类:org.apache.hive.jdbc.HiveDriver
  2. 通过ctrl+c方式断开/异常断开
      对于Hive JDBC这种连接方式而言,不存在ctrl+c直接断开RPC连接这种说法。我们通过程序测试的话,ctrl+c直接退出或kill的是我们的程序[JVM],断开程序之后session连接自然断开。
      这里的处理方式如Beeline类似,同样是HiveServer2通过session来管理任务的退出。

4. 问题是否解决

通过以上分析可以得出结论:
1. Hive Cli模式下,直接Kill -9结束当前的Hive进程,是无法让运行在Yarn上的任务退出;
2. 使用Beeline或JDBC连接Hive时,可以设置hive.server2.close.session.on.disconnect=false,来阻止session的关闭操作[未测试];

  我们基本获知了Hive通过几种方式访问集群资源时,访问的方式、驱动主类、程序退出资源释放的流程等内容。因此,以上的分析基本上解答了我们的问题。即除了Hive CLI方式通过[Kill -9]直接退出JVM之外,其他情况基本上都会停止Yarn任务的运行。

猜你喜欢

转载自blog.csdn.net/aWDac/article/details/80932814