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

读程序就要从main函数读起,从而了解程序的流程。所以我首先从CliDriver这个类开始。

一 ,主函数

  public static void main(String[] args) throws Exception {
    int ret = new CliDriver().run(args);
    System.exit(ret);
  }
  1. String[] args 是运行的时候由外部命令穿进来的参数;
  2. System.exit(ret)明显是退出函数
   /** 
     * Terminates the currently running Java Virtual Machine. The 
     * argument serves as a status code; by convention, a nonzero status 
     * code indicates abnormal termination. 
     * <p> 
     * This method calls the <code>exit</code> method in class 
     * <code>Runtime</code>. This method never returns normally. 
     * <p> 
     * The call <code>System.exit(n)</code> is effectively equivalent to 
     * the call: 
     * <blockquote><pre> 
     * Runtime.getRuntime().exit(n) 
     * </pre></blockquote> 
     * 
     * @param status exit status. 
     * @throws SecurityException 
     * if a security manager exists and its <code>checkExit</code> 
     * method doesn't allow exit with the specified status. 
     * @see java.lang.Runtime#exit(int) 
     */  
    public static void exit(int status) {  
        Runtime.getRuntime().exit(status);  
    }

这个是System.exit()的源码,翻译它第一段的注释-“终止当前正在运行的java虚拟机。这个参数作为状态码;按惯例,非零状态代码表示异常终止。与return不同,return只是退回到上一层,exit将会退出到最外层。
3. 最重要的一句就是建立新的CliDriver对象,并调用他的run()函数,下来我们进入类的构造函数。

二,构造函数

  public CliDriver() {
    SessionState ss = SessionState.get();
    conf = (ss != null) ? ss.getConf() : new Configuration();
    Log LOG = LogFactory.getLog("CliDriver");
    if (LOG.isDebugEnabled()) {
      LOG.debug("CliDriver inited with classpath " + System.getProperty("java.class.path"));
    }
    console = new LogHelper(LOG);
  }

首先创建了一个SessionState类,并调用get()函数; SessionState封装了一个会话的关联的数据,包括配置信息HiveConf,输入输出流,指令类型,用户名称、IP地址等等。SessionState 是一个与线程关联的静态本地变量ThreadLocal,任何一个线程都对应一个SessionState,能够在Hive代码的任何地方获取到(大量被使用到),以返回用户相关或者配置信息等。来看源码:

  /**
    get the current session.
   */
  public static SessionState get() {
    return tss.get().state;
  }
 /**
   * Singleton Session object per thread.
   *
   **/
  private static ThreadLocal<SessionStates> tss = new ThreadLocal<SessionStates>() {
    @Override
    protected SessionStates initialValue() {
      return new SessionStates();
    }
  };
  1. 注释中说get()是在获取当前会话,返回了一个对象tss的调用,有关tss建立代码我也贴出来了。有关Threadlocal详见:http://blog.csdn.net/lufeng20/article/details/24314381/ 也就是说,Threadlocal 是为了解决多线程当中变量访问冲突的问题,它为每一个线程均拷贝了一份副本,线程可以对此副本进行随意的修改而不会与其他线程发生冲突。其中get()方法返回当前线程所对应的线程局部变量,其初始值为null;

  2. 在定义好conf和log之后;System.getProperty 可以获得可执行.class文件的路径以及引入的jar包路径。LogHelper 目前看来就是一个管理日志的类;

  3. 在此提出一个问题,为什么在工程中要使用log呢?有什么帮助呢?

三,run()函数

    OptionsProcessor oproc = new OptionsProcessor();
    if (!oproc.process_stage1(args)) {
      return 1;
    }

建立OptionsProcessor类的对象oproc,并且对命令进行初步的解析,提取-e -h hiveconf hivevar等参数信息,设置用户提供的系统和Hive环境变量。具体的细节可以在OptionsProcessor这个类中查看到。

 boolean logInitFailed = false;
    String logInitDetailMessage;
    try {
      logInitDetailMessage = LogUtils.initHiveLog4j();
    } catch (LogInitializationException e) {
      logInitFailed = true;
      logInitDetailMessage = e.getMessage();
    }

初始化Log4j日志组件;使用的日志的好处是什么呢?

    CliSessionState ss = new CliSessionState(new HiveConf(SessionState.class));
    ss.in = System.in;
    try {
      ss.out = new PrintStream(System.out, true, "UTF-8");
      ss.info = new PrintStream(System.err, true, "UTF-8");
      ss.err = new CachingPrintStream(System.err, true, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      return 3;
    }

使用HiveConf实例化CliSessionState,CliSessionState 继承了SessionState类,,创建了输入输出流,也创建了一些记录用户输入的字符串,比如:fileName,cmdProperties,在实例化的过程中,主要是用来记录HiveConf。

 if (!oproc.process_stage2(ss)) {
      return 2;
    }
 以下是它在OptionsProcessor类中的详细定义:
 public boolean process_stage2(CliSessionState ss) {
    ss.getConf();

    if (commandLine.hasOption('H')) {
      printUsage();
      return false;
    }

    ss.setIsSilent(commandLine.hasOption('S'));

    ss.database = commandLine.getOptionValue("database");

    ss.execString = commandLine.getOptionValue('e');

    ss.fileName = commandLine.getOptionValue('f');

    ss.setIsVerbose(commandLine.hasOption('v'));

    String[] initFiles = commandLine.getOptionValues('i');
    if (null != initFiles) {
      ss.initFiles = Arrays.asList(initFiles);
    }

    if (ss.execString != null && ss.fileName != null) {
      System.err.println("The '-e' and '-f' options cannot be specified simultaneously");
      printUsage();
      return false;
    }

    if (commandLine.hasOption("hiveconf")) {
      Properties confProps = commandLine.getOptionProperties("hiveconf");
      for (String propKey : confProps.stringPropertyNames()) {
        ss.cmdProperties.setProperty(propKey, confProps.getProperty(propKey));
      }
    }

    return true;
  }

从这里可以看出,process_stage1只是对命令行进行了一个初步的解析,在process_stage2中对CliSessionState的成员进行了赋值,但这和process-stage1有什么联系吗?不应该执行完process_stage1这个函数就return了吗?程序是不断循环执行的吗?

    if (!ss.getIsSilent()) {
      if (logInitFailed) {
        System.err.println(logInitDetailMessage);
      } else {
        SessionState.getConsole().printInfo(logInitDetailMessage);
      }
    }

在允许打印的模式下,如果初始化日志失败就打印错误信息。

    HiveConf conf = ss.getConf();
    for (Map.Entry<Object, Object> item : ss.cmdProperties.entrySet()) {
      conf.set((String) item.getKey(), (String) item.getValue());
      ss.getOverriddenConfigurations().put((String) item.getKey(), (String) item.getValue());
    }

将用户命令行输入的配置信息和变量等覆盖HiveConf的默认值

    prompt = conf.getVar(HiveConf.ConfVars.CLIPROMPT);
    prompt = new VariableSubstitution().substitute(conf, prompt);
    prompt2 = spacesForString(prompt);

注释中说:read prompt configuration and substitute variables.也就是读取配置提示和替换变量。这一步有什么作用呢?

    SessionState.start(ss);
    try {
      return executeDriver(ss, conf, oproc);
    } finally {
      ss.close();
    }
  }

设置会话状态,然后执行CLI程序,即excuteDriver().

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值