Hive源码分析(1)——CLi输入处理

Hive源码分析(一)——CLi输入处理

北京时间:2020年04月28日10:30

环境Hive3.1.1

1、程序栈主要执行流程

  1. main:683, CliDriver (org.apache.hadoop.hive.cli)

    程序入口:

    public static void main(String[] args) throws Exception {
        int ret = new CliDriver().run(args);
        System.exit(ret);
    }
    
  2. run:759, CliDriver (org.apache.hadoop.hive.cli)

    public  int run(String[] args) throws Exception {
    	// 参数解析OptionsProcessor( -f -hiveconf -d -i 等等参数解析) 
        OptionsProcessor oproc = new OptionsProcessor();
        if (!oproc.process_stage1(args)) {
            return 1;
        }
    
         // log4j 日志加载 initHiveLog4j
        // ( 此时会调用HiveConf 并将里面的一些静态变量初始化了,获取日志的一些配置)
        boolean logInitFailed = false;
        String logInitDetailMessage;
        try {
            logInitDetailMessage = LogUtils.initHiveLog4j();
        } catch (LogInitializationException e) {
            logInitFailed = true;
            logInitDetailMessage = e.getMessage();
        }
    	
        // 创建CliSessionState 、HiveConf
        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;
        }
    	
        // 继续解析参数(-S -database -e -f -v -i等参数,并将其设置到CliSessionState ss)
        if (!oproc.process_stage2(ss)) {
            return 2;
        }
    
        // 当前会话是否在 silent 模式运行
        // 如果不是 silent 模式,info 级打在日志中的消息,都将以标准错误流的形式输出到控制台。
        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());
        }
    
        // 读取提示符配置(hive.cli.prompt)和替换变量
        prompt = conf.getVar(HiveConf.ConfVars.CLIPROMPT);
        prompt = new VariableSubstitution(new HiveVariableSource() {
            @Override
            public Map<String, String> getHiveVariable() {
                return SessionState.get().getHiveVariables();
            }
        }).substitute(conf, prompt);
        prompt2 = spacesForString(prompt);
    	
        // 这里的if else 方法都是调用org.apache.hadoop.hive.ql.session.SessionState#start方法
        // 只是传入的参数不同
        if (HiveConf.getBoolVar(conf, ConfVars.HIVE_CLI_TEZ_SESSION_ASYNC)) {
            // 传入:start(startSs, true, console);
            SessionState.beginStart(ss, console);
        } else {
           	// 传入:start(startSs, false, null);
            SessionState.start(ss);
        }
    
        // 更新并且打印当前线程的名称:
        ss.updateThreadName();
    
       	// 创建和初始化视图注册缓存
        HiveMaterializedViewsRegistry.get().init();
    
        try {
            /****** 核心方法:下面开始执行 ******/
            return executeDriver(ss, conf, oproc);
        } finally {
            ss.resetThreadName();
            ss.close();
        }
    }
    

    小结:主要是处理Hive命令参数

    1. 解析Hive命令后接参数
    2. 同时设置到到HiveConf
    3. 执行命令
  3. executeDriver:821, CliDriver (org.apache.hadoop.hive.cli)

    /**
     * 执行命令
     * @param ss CliSessionState
     * @param conf HiveConf
     * @param oproc 命令行所设置的参数
     * @return status 返回执行状态
     * @throws Exception
     */
    private int executeDriver(CliSessionState ss, HiveConf conf, OptionsProcessor oproc)
        throws Exception {
    	// 创建CliDriver,并且设置命令行中所设置的参数
        CliDriver cli = new CliDriver();
        cli.setHiveVariables(oproc.getHiveVariables());
    
        // 使用指定的数据库(会自动执行:use hive_temp(具体的数据库名);)
        cli.processSelectDatabase(ss);
    
        // 初始化指定的SQL脚本文件
        cli.processInitFiles(ss);
    	
        // 命令行使用-e参数(不进入hive模式,直接执行SQL字符串)
        if (ss.execString != null) {
            int cmdProcessStatus = cli.processLine(ss.execString);
            // 返回结果
            return cmdProcessStatus;
        }
    
        try {
            // 命令行使用-f参数(不进入hive模式,直接执行SQL脚本文件)
            if (ss.fileName != null) {
                /****** 处理SQL文件 ******/
                return cli.processFile(ss.fileName);
            }
        } catch (FileNotFoundException e) {
            System.err.println("Could not open input file for reading. (" + e.getMessage() + 
                               																	")");
            return 3;
        }
        if ("mr".equals(HiveConf.getVar(conf, ConfVars.HIVE_EXECUTION_ENGINE))) {
            console.printInfo(HiveConf.generateMrDeprecationWarning());
        }
    
        setupConsoleReader();
    	// 下面是在hive模式中
        String line;
        int ret = 0;
        // 用于多行输入拼接为完整的一行SQL
        String prefix = "";
        // hive.cli.print.current.db:是否显示当前数据
        String curDB = getFormattedDb(conf, ss);
        String curPrompt = prompt + curDB;
        String dbSpaces = spacesForString(curDB);
    	// 从控制台读取每一行
        while ((line = reader.readLine(curPrompt + "> ")) != null) {
            if (!prefix.equals("")) {
                prefix += '\n';
            }
            // 遇到注释跳过
            if (line.trim().startsWith("--")) {
                continue;
            }
            // 当前SQL遇到了结尾的一行(以";"为结尾)
            // 注意:这里只是以";"为结尾,如果";"在语句中间此处没有处理分割
            if (line.trim().endsWith(";") && !line.trim().endsWith("\\;")) {
                // 拼接SQL
                line = prefix + line;
                /****** 执行SQL(核心方法) ******/
                ret = cli.processLine(line, true);
                // 重置拼接的SQ
                prefix = "";
                curDB = getFormattedDb(conf, ss);
                curPrompt = prompt + curDB;
                dbSpaces = dbSpaces.length() == curDB.length() ? dbSpaces : 
                															spacesForString(curDB);
            } else {
                // 没有SQL结束,继续拼接
                prefix = prefix + line;
                curPrompt = prompt2 + dbSpaces;
                continue;
            }
        }
    
        return ret;
    }
    

    小结:根据参数处理相应不同输入类型的SQL

    1. 是否是Hive命令执行SQL字符串,直接处理SQL字符串,返回结果(processLine方法)
    2. 是否是HIve命令执行SQL文件,直接处理SQL文件,返回结果(processFile方法)
    3. 只能是Hive命令行模式,处理输入SQL(processLine方法)
  4. processLine:402, CliDriver (org.apache.hadoop.hive.cli)

    public int processLine(String line, boolean allowInterrupting) {
        SignalHandler oldSignal = null;
        Signal interruptSignal = null;
    	// 是否允许中断
        if (allowInterrupting) {
            // 当前执行Ctrl+C
            interruptSignal = new Signal("INT");
            oldSignal = Signal.handle(interruptSignal, new SignalHandler() {
                private boolean interruptRequested;
    
                @Override
                public void handle(Signal signal) {
                    boolean initialRequest = !interruptRequested;
                    interruptRequested = true;
    
                     // 第一次Ctrl+C,关闭当前的java程序
                    if (!initialRequest) {
                        console.printInfo("Exiting the JVM");
                        System.exit(127);
                    }
    
                    // 中断信息提示
                    console.printInfo("Interrupting... Be patient, this might take some time.");
                    console.printInfo("Press Ctrl+C again to kill JVM");
    
                    // 第一次Ctrl+C,关闭所有在执行的MR jobs
                    HadoopJobExecHelper.killRunningJobs();
                    TezJobExecHelper.killRunningJobs();
                    HiveInterruptUtils.interrupt();
                }
            });
        }
    
        try {
            int lastRet = 0, ret = 0;
    
            // 对命令行字符串,用';'分割开来,去除';'
            // 不能使用split方法,直接使用split可能会导致依旧含有';'
            List<String> commands = splitSemiColon(line);
    
            String command = "";
            // 遍历传入的命令串
            for (String oneCmd : commands) {
                // 如果是'\'结尾,则删除掉'\',并且加上";"
                if (StringUtils.endsWith(oneCmd, "\\")) {
                    command += StringUtils.chop(oneCmd) + ";";
                    continue;
                } else {
                    command += oneCmd;
                }
                // 空字符串,跳过
                if (StringUtils.isBlank(command)) {
                    continue;
                }
    			 /****** 执行SQL(核心方法) ******/
                ret = processCmd(command);
                // 重置命令
                command = "";
                // 最后一次执行结果
                lastRet = ret;
                // 是否忽略错误
                boolean ignoreErrors = HiveConf.getBoolVar(conf, 
                                                           	HiveConf.ConfVars.CLIIGNOREERRORS);
                // 执行出错,并且不忽略错误,返回错误结果
                if (ret != 0 && !ignoreErrors) {
                    return ret;
                }
            }
            return lastRet;
        } finally {
            // Once we are done processing the line, restore the old handler
            if (oldSignal != null && interruptSignal != null) {
                Signal.handle(interruptSignal, oldSignal);
            }
        }
    }
    

    小结:判断中断,处理SQL字符串

    1. 判断是否允许中断
    2. 处理SQL字符串
    3. 继续执行SQL
  5. processCmd:127, CliDriver (org.apache.hadoop.hive.cli)

    public int processCmd(String cmd) {
        CliSessionState ss = (CliSessionState) SessionState.get();
        ss.setLastCommand(cmd);
    
        ss.updateThreadName();
    
        // 刷新打印流,因此它不包括最后一个命令的输出
        ss.err.flush();
        String cmd_trimmed = HiveStringUtils.removeComments(cmd).trim();
        String[] tokens = tokenizeCmd(cmd_trimmed);
        int ret = 0;
    
        // 处理退出程序
        if (cmd_trimmed.toLowerCase().equals("quit") || 
            											cmd_trimmed.toLowerCase().equals("exit")) {
            // 如果我们已经走到这一步,要么前面的命令都成功,要么这是退出命令行
            // 无论哪种情况,这都算是成功运行
            ss.close();
            System.exit(0);
    
        } 
        // 在Hive模式下执行SQL脚本
        else if (tokens[0].equalsIgnoreCase("source")) {
            String cmd_1 = getFirstCmd(cmd_trimmed, tokens[0].length());
            cmd_1 = new VariableSubstitution(new HiveVariableSource() {
                @Override
                public Map<String, String> getHiveVariable() {
                    return SessionState.get().getHiveVariables();
                }
            }).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 {
                    /****** 处理SQL文件 ******/
                    ret = processFile(cmd_1);
                } catch (IOException e) {
                    console.printError("Failed processing file "+ cmd_1 +" "+ 
                                       e.getLocalizedMessage(), stringifyException(e));
                    ret = 1;
                }
            }
        } 
        // 对于shell命令的处理
        else if (cmd_trimmed.startsWith("!")) {
            // for shell commands, use unstripped command
            String shell_cmd = cmd.trim().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 { 
            try {
                // 进入本地模式,也就是Hive模式下的命令行输入SQL
                try (CommandProcessor proc = CommandProcessorFactory.get(tokens, 
                                                                         (HiveConf) conf)) {
                    if (proc instanceof IDriver) {
                        /****** 继续处理 ******/
                        ret = processLocalCmd(cmd, proc, ss);
                    } else {
                        ret = processLocalCmd(cmd_trimmed, proc, ss);
                    }
                }
            } catch (SQLException e) {
                console.printError("Failed processing command " + tokens[0] + " " + 											e.getLocalizedMessage(), 
                                   org.apache.hadoop.util.StringUtils.stringifyException(e));
                ret = 1;
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        ss.resetThreadName();
        return ret;
    }
    

    小结:处理命令

    1. 继续处理命令,如果使用SQL文件,则执行processFile方法
    2. 本地SQL命令,则执行processLocalCmd方法
  6. 下面来看下对SQL文件的处理

    org.apache.hadoop.hive.cli.CliDriver#processFile

    public int processFile(String fileName) throws IOException {
        Path path = new Path(fileName);
        FileSystem fs;
        if (!path.toUri().isAbsolute()) {
            fs = FileSystem.getLocal(conf);
            path = fs.makeQualified(path);
        } else {
            fs = FileSystem.get(path.toUri(), conf);
        }
        BufferedReader bufferReader = null;
        int rc = 0;
        try {
            bufferReader = new BufferedReader(new InputStreamReader(fs.open(path)));
            // 到上面这一步为止都是在读取SQL文件
            
            /****** 处理SQL文件 ******/
            rc = processReader(bufferReader);
        } finally {
            IOUtils.closeStream(bufferReader);
        }
        return rc;
    }
    

    org.apache.hadoop.hive.cli.CliDriver#processReader

    public int processReader(BufferedReader r) throws IOException {
        String line;
        StringBuilder qsb = new StringBuilder();
    	// 读取文件中的每一行
        while ((line = r.readLine()) != null) {
            // 忽略SQL文件中的注释
            if (! line.startsWith("--")) {
                // SQL 拼接
                qsb.append(line + "\n");
            }
        }
    	/****** 这里还是调用processLine方法 ******/
        return (processLine(qsb.toString()));
    }
    

    小结:只是添加了一个文件的读取,最后还是调用org.apache.hadoop.hive.cli.CliDriver#processReader方法

    到这里为止,对于输入的处理已经完成,下一步便是对SQL的编译执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Hive是一个大数据分析的开源平台,它提供了很多的综合应用案例,其中之一是用户学历查询。用户学历查询案例中,我们可以使用Hive从数据仓库中读取关于用户的数据,然后通过分析和聚合这些数据,来确定用户的学历信息。在这个过程中,Hive提供了强大的SQL语句支持,以及丰富的函数库,可以满足各种复杂的分析需求。 ### 回答2: 近日,一家大型人力资源公司因为工作中频繁查询员工学历信息而决定使用hive技术来进行数据的处理分析,通过hive的综合应用,实现了更加高效便捷的用户学历查询。 此次项目中,该公司首先进行了数据清洗和处理,将员工学历相关信息从不同的数据源中收集并整合,统一存储在hive的数据仓库中。接着,通过hiveQL语言对数据进行查询和筛选,实现了仅需几秒钟即可得到用户学历信息的目标。使用hive的好处是能够快速处理海量数据,提高查询效率,同时还能实现数据分析和统计,并可视化展示数据结果。 在具体的操作中,该公司将原始数据进行拆分和转换,把不同的表进行组合和分析。同时,在处理数据过程中还运用了一些hadoop生态体系的其他组件,比如pig和spark等工具,以提高效率和处理这些数据所需的时间。在处理完成之后,用户可以通过hive对用户的学历进行查询,根据自己的需要,可以根据职位、学历、工作年限等限定条件来查询,大大地提高了查询精度,也让招聘和人事等相关部门的工作变得更加轻松便捷。 总的来说,该项目成功运用了hive技术,实现了用户学历查询的目标,提升了工作效率和数据查询的准确度。而且此次项目的经验和教训也可以为其他公司和企业在hive技术的运用方面提供借鉴和参考。 ### 回答3: Hive是Hadoop生态系统中的一项重要技术,它是基于Hadoop的MapReduce框架的数据仓库解决方案,可以进行大规模数据的存储和处理。而学历查询是一个典型的数据查询场景,由于学历查询需要对大量的数据进行快速高效的查询和分析,因此使用Hive进行学历查询可以大大提高查询效率和数据处理能力。 以学历查询为例,如果我们需要查询某个区域内所有人的学历情况,我们需要从海量的数据中筛选出相关的信息,然后进行分析和统计,这个过程需要耗费大量的时间和精力。但是如果使用Hive,我们可以通过Hive的SQL查询语句来快速实现数据查询和分析。 具体来说,如果我们要查询一个城市的所有居民的学历情况,首先要准备好相应的数据表或数据集,然后利用Hive的相关命令将数据导入到Hive表中。在数据导入完成后,我们可以使用类似于SQL的语句来实现查询。例如,我们可以使用Hive的SELECT语句和GROUP BY语句来实现按学历等级进行分组并统计各组的人数。 当然,如果需要更加复杂的查询和分析任务,我们也可以使用Hive进行更多的数据处理和计算任务。例如,Hive支持使用自定义的UDF函数,可以对数据实现更加灵活和精细化的处理。 总而言之,Hive作为一种分布式数据存储和查询解决方案,可以为学历查询等数据处理场景提供快速、高效和可扩展的解决方案。通过合理的数据分析和查询,可以帮助我们更好地了解数据背后的真相,为业务决策提供更多有效的参考和支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值