详解Hive-CliDriver续——细读Hive源码(三)

来源:互联网 发布:中小企业员工流失数据 编辑:程序博客网 时间:2024/06/06 04:55

上次我们阅读了main(),run(),和CLiDriver的构造函数;接上,我们该阅读executeDriver函数了。在进入executeDriver之前,我们可以认为Hive处理的是用户进入Hive程序的指令,到此用户已经进入了Hive,Cli的Driver 将不断读取用户的HiveQL语句并解析,提交给Driver。executeDriver 函数内部核心的代码是通过while循环不断按行读取用户的输入,然后调用ProcessLine拼接一条命令cmd,传递给processCmd处理用户输入。executeDriver 和ProcessLine这两个函数有空再细说,下面就来看看processCmd函数。

    CliSessionState ss = (CliSessionState) SessionState.get();    ss.setLastCommand(cmd);    // Flush the print stream, so it doesn't include output from the last command    ss.err.flush();    String cmd_trimmed = cmd.trim();    String[] tokens = tokenizeCmd(cmd_trimmed);   // tokenizeCmd   private String[] tokenizeCmd(String cmd) {    return cmd.split("\\s+");  }

首先是设置当前clisession的指令,将用户输入的指令从空格,制表符等出断开(tokenizeCmd函数),得到token数组。
其中这个tokenizeCmdhan()函数使用了split(“\s+”),这个使用了java的正则表达式,’\s’ 的意思是匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。为什么使用’\’呢,因为’\’是转移字符 的前缀,想要表示’\’本身,就得使用’\’。

    if (cmd_trimmed.toLowerCase().equals("quit") || cmd_trimmed.toLowerCase().equals("exit")) {      // if we have come this far - either the previous commands      // are all successful or this is command line. in either case      // this counts as a successful run      ss.close();      System.exit(0);    } else if (tokens[0].equalsIgnoreCase("source")) {      String cmd_1 = getFirstCmd(cmd_trimmed, tokens[0].length());      cmd_1 = new VariableSubstitution().substitute(ss.getConf(), cmd_1);      File sourceFile = new File(cmd_1);      if (! sourceFile.isFile()){        console.printError("File: "+ cmd_1 + " is not a file.");        ret = 1;      } else {        try {          ret = processFile(cmd_1);        } catch (IOException e) {          console.printError("Failed processing file "+ cmd_1 +" "+ e.getLocalizedMessage(),            stringifyException(e));          ret = 1;        }      }    } else if (cmd_trimmed.startsWith("!")) {      String shell_cmd = cmd_trimmed.substring(1);      shell_cmd = new VariableSubstitution().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;      }    } 

这段程序中分了三种情况:
quit或exit: 关闭回话,退出hive
source: 调用processFile读取文件
! 开头: 调用Linux系统的shell执行指令

  else { // local mode      try {        CommandProcessor proc = CommandProcessorFactory.get(tokens, (HiveConf) conf);        ret = processLocalCmd(cmd, proc, ss);      } catch (SQLException e) {        console.printError("Failed processing command " + tokens[0] + " " + e.getLocalizedMessage(),          org.apache.hadoop.util.StringUtils.stringifyException(e));        ret = 1;      }    }

这段程序和上面的同属于一个if-else语句中,从这里可以进入本地模式,也就是我们常常使用的Hsql语句。

  • 首先看一下CommandProcessor这个类
public interface CommandProcessor {  void init();  CommandProcessorResponse run(String command) throws CommandNeedRetryException;}

这是一个接口类,CommandProcessorFactory 根据用户指令生成的tokens和配置文件,返回CommandProcessor的一个具体实现。

 public static CommandProcessor get(String[] cmd, HiveConf conf)      throws SQLException {    CommandProcessor result = getForHiveCommand(cmd, conf);    if (result != null) {      return result;    }    if (isBlank(cmd[0])) {      return null;    } else {      if (conf == null) {        return new Driver();      }      Driver drv = mapDrivers.get(conf);      if (drv == null) {        drv = new Driver();        mapDrivers.put(conf, drv);      }      drv.init();      return drv;    }  }
  • 其中getForHiveCommand函数首先根据tokens的第一个字串,也就是用户输入指令的第一个单词,在HiveCommand这个enum中定义的一些非SQL查询操作集合中进行匹配,确定相应的HiveCommand类型。在依据HiveCommand选择合适的CommandProcessor实现方式,比如dfs命令对应的DFSProcessor,set命令对应的SetProcessor等,如果用户输入的是诸如select之类的SQL查询,getForHive Command 返回 null,直接在get函数中根据配置文件conf选择或者生成一个Driver类实例,并作为CommandProcessor返回。

  • 在 CommandProcessorFactory 中可以看到对不同指令的操作,代码如下:

 switch (hiveCommand) {      case SET:        return new SetProcessor();      case RESET:        return new ResetProcessor();      case DFS:        SessionState ss = SessionState.get();        return new DfsProcessor(ss.getConf());      case ADD:        return new AddResourceProcessor();      case LIST:        return new ListResourceProcessor();      case DELETE:        return new DeleteResourceProcessor();      case COMPILE:        return new CompileProcessor();      case RELOAD:        return new ReloadProcessor();      case CRYPTO:        try {          return new CryptoProcessor(SessionState.get().getHdfsEncryptionShim(), conf);        } catch (HiveException e) {          throw new SQLException("Fail to start the command processor due to the exception: ", e);        }      default:        throw new AssertionError("Unknown HiveCommand " + hiveCommand);    }
  • 到此Hive对用户的一个指令cmd,配置了会话状态CliSessionState,选择了一个合适的CommandProcessor, CliDriver 将进行他的最后一步操作,提交用户的cmd到指定的CommandProcessor,并获取结果。这一切都是在processLocalCmd中执行的。
do {      try {        needRetry = false;        if (proc != null) {          //如果CommandProcessor是Driver实例          if (proc instanceof Driver) {            Driver qp = (Driver) proc;            //获取标准输出流,打印结果信息            PrintStream out = ss.out;            long start = System.currentTimeMillis();            if (ss.getIsVerbose()) {              out.println(cmd);            }            qp.setTryCount(tryCount);            //driver实例运行用户指令,获取运行结果响应码            ret = qp.run(cmd).getResponseCode();            if (ret != 0) {              qp.close();              return ret;            }            // 统计指令的运行时间            long end = System.currentTimeMillis();            double timeTaken = (end - start) / 1000.0;            ArrayList<String> res = new ArrayList<String>();             //打印查询结果的列名称            printHeader(qp, out);            // 打印查询结果            int counter = 0;            try {              if (out instanceof FetchConverter) {                ((FetchConverter)out).fetchStarted();              }              while (qp.getResults(res)) {                for (String r : res) {                  out.println(r);                }                counter += res.size();                res.clear();                if (out.checkError()) {                  break;                }              }            } catch (IOException e) {              console.printError("Failed with exception " + e.getClass().getName() + ":"                  + e.getMessage(), "\n"                  + org.apache.hadoop.util.StringUtils.stringifyException(e));              ret = 1;            }            //关闭结果            int cret = qp.close();            if (ret == 0) {              ret = cret;            }            if (out instanceof FetchConverter) {              ((FetchConverter)out).fetchFinished();            }            console.printInfo("Time taken: " + timeTaken + " seconds" +                (counter == 0 ? "" : ", Fetched: " + counter + " row(s)"));          } else {            //如果proc不是Driver,也就是用户执行的是非SQL查询操作,直接执行语句,不执行FetchResult的操作            String firstToken = tokenizeCmd(cmd.trim())[0];            String cmd_1 = getFirstCmd(cmd.trim(), firstToken.length());            if (ss.getIsVerbose()) {              ss.out.println(firstToken + " " + cmd_1);            }            CommandProcessorResponse res = proc.run(cmd_1);            if (res.getResponseCode() != 0) {              ss.out.println("Query returned non-zero code: " + res.getResponseCode() +                  ", cause: " + res.getErrorMessage());            }            ret = res.getResponseCode();          }        }      } catch (CommandNeedRetryException e) {        //如果执行过程中出现异常,修改needRetry标志,下次循环是retry。        console.printInfo("Retry query with a different approach...");        tryCount++;        needRetry = true;      }    } while (needRetry);
  • 如果程序的needRetry为false,那么意味着程序正常执行,整个过程包括对命令的解析,执行和返回。程序的具体细节在后续章节中会继续讲到。
原创粉丝点击