通过Hive JDBC提交的查询, 如何获取其在Yarn上的Application ID

  • 数据平台上需要封装Hive查询,只提供API给业务方使用,代码中通过Hive JDBC完成将查询语句向Hive提交,等待执行完成,结果解析的功能.
  • 用户提交查询之后意识到查询语句错误(非语法错误),不想等待错误的语句执行完成后再次提交. 由于用户没有yarn client的权限,kill task的操作也需要通过API提供,因此在提交查询语句时需要获取其在Yarn上的Application Id.
  • Hive在执行层面加装了Tez,不是那个最初的Hive了.

两种思路

在网上研究了一番之后,思路主要有两种:

  1. 利用HiveStatement获取查询日志后进行解析,得到Application Id
  2. 提交查询的时候,为查询附上唯一标识符,通过该标识符找到Application Id

第一种思路实现起来简单,但是不够优雅,而且在并发查询的情况下会是什么效果没有测试过.

第二种查询由于Tez的原因,尝试了很多种方案,都不能有效的设置查询标识符.

该功能测试的通用连接代码如下:

private static String driverName = "org.apache.hive.jdbc.HiveDriver";
Class.forName(driverName);

String url = "jdbc:hive2://:10000/";

Properties info = new Properties();
info.setProperty("user", "bigdata");
info.setProperty("password", "******");

Connection conn = DriverManager.getConnection(url, info);
HiveStatement stmt = (HiveStatement) conn.createStatement();
String table = "table_name";
ResultSet result = stmt.executeQuery("SELECT COUNT(*) FROM " + table);

解析查询日志

  • 需要另外起一个线程用于日志的获取(等到查询执行完,就不用实现kill的功能了)
  • 并发查询的时候,如何确认获取到的日志就是某个查询的日志呢?

日志解析的Java代码如下:

String yarn_app_id = "";
for (String log : stmt.getQueryLog()) {
    if (log.contains("App id")) {
        yarn_app_id = log.substring(log.indexOf("App id") + 7, log.length() - 1);
    }
}
System.out.println(yarn_app_id);

通过设置查询的唯一标识符

这个办法想起来简单,实际操作起来,绕了十万八千里的路.

最开始拍脑袋想到去修改Application Name,一般的以为设置mapred.job.name这个参数就好了. 要想在运行时修改配置参数,需要在hive-site.xml中加入配置项hive.security.authorization.sqlstd.confwhitelisthive.security.authorization.sqlstd.confwhitelist.append.

我的配置最终如下:

<property>
     <name>hive.security.authorization.sqlstd.confwhitelist.appendname>
     <value>hive.*|mapred.*|tez.*|queue.*value>
property>
<property>
     <name>hive.security.authorization.sqlstd.confwhitelistname>
     <value>hive.*|mapred.*|tez.*|queue.*value>
property>

但是mapred.job.name这个配置并不能生效. 类似的配置像hive.query.name,也没有作用. Debug了一下,发现在HiveDriver层是能够读取并写入conf的,而最终没有生效,不能确定在Tez层发生了什么.

从网上的资料来看,Tez层设置的Application Name是按照HIVE-来设置的. 这个配置项虽然不推荐修改,但是通过命令行提供Hive(On Tez)查询的时候,它是能够生效的。但是在代码中传入配置项,来实例化Connection,却依然不起作用.

凭感觉判断肯定和Tez有关,网上的解答不多,找了相关的一些FAQ和Mail,大概也是这样的判断. 不过这部分都是都还只是猜测,于是想试试看能不能在代码中成功修改tez的配置. 最简单的锁定在queue.name这项上. 直接改mapred.job.queue.name并没什么卵用,但是在尝试通过tez.queue.name配置项进行修改时,奇迹出现了.

这个尝试可以基本断定,Hive On Tez在通过Hive JDBC提交的时候,只能成功读取(或者成功设置)Tez支持的配置项。Tez 0.9.1支持的所有配置项如下:

Tez 0.9.1配置项

在该文档中可以看到之前用来修改提交队列的配置:

Property Name Default Value Description
tez.queue.name null String value. The queue name for all jobs being submitted from a given client.

不幸的是,TezConfiguration里没有能够修改Application Name的配置项. 正当万念俱灰之际,正是船到桥头自然直,柳暗花明又一村. 虽然没找到能够修改Application Name的配置项,但是在模糊查询的时候,发现了这个配置tez.application.tags. 于是顺藤摸瓜的,研究了一下Yarn对于Application Tag的支持.

Property Name Default Value Description
tez.application.tag null String value. Tags for the job that will be passed to YARN at submission time. Queries to YARN for applications can filter on these tags.

尝试了一下,通过JDBC的方式成功设置.Yarn对于tag的使用应该是用来过滤出某组应用的,这里我就曲线救国,借用一下用来存放查询的唯一标识符.

首先,启动Yarn Client:

YarnClient client = YarnClient.createYarnClient();
Configuration conf = new Configuration();
// conf.set();
client.init(conf);
client.start();

提交作业前设置property:

String url = "jdbc:hive2://:10000/";

Properties info = new Properties();
info.setProperty("user", "bigdata");
info.setProperty("password", "******");
info.setProperty("hiveconf:hive.execution.engine", "tez");
info.setProperty("hiveconf:tez.application.tags", "hive-client-job-test");

获取ApplicationReport:

Set applicationTypes = Sets.newHashSet();
applicationTypes.add("TEZ");

Set applicationTags = Sets.newHashSet();
applicationTags.add("hive-client-job-test");

Set applicationStates = Sets.newHashSet();
applicationStates.add(YarnApplicationState.ACCEPTED);
applicationStates.add(YarnApplicationState.FINISHED);
applicationStates.add(YarnApplicationState.RUNNING);

EnumSet enumStates = Sets.newEnumSet(applicationStates, YarnApplicationState.class);

List reports = client.getApplications(applicationTypes, enumStates, applicationTags);
for (ApplicationReport report : reports) {
    System.out.println(report.getApplicationId());
}

虽然client.getApplications()方法返回的是一个List,但是因为我的Tag是唯一生成的,所以实际上只会返回一个Application Id

done

总结

这种方法是我觉得相对来说比较优雅的解决方案了,暂时没有想出更好的办法了,如果有更好方式了话,欢迎评论交流!

你可能感兴趣的:(研发之路)