通過Hive JDBC提交的查詢, 如何獲取其在Yarn上的Application ID

數據平臺上需要封裝Hive查詢,只提供API給業務方使用,代碼中通過Hive JDBC完成將查詢語句向Hive提交,等待執行完成,結果解析的功能.
用戶提交查詢之後意識到查詢語句錯誤(非語法錯誤),不想等待錯誤的語句執行完成後再次提交. 由於用戶沒有yarn client的權限,kill task的操作也需要通過API提供,因此在提交查詢語句時需要獲取其在Yarn上的Application Id.
Hive在執行層面加裝了Tez,不是那個最初的Hive了.
兩種思路
在網上研究了一番之後,思路主要有兩種:

利用HiveStatement獲取查詢日誌後進行解析,得到Application Id
提交查詢的時候,爲查詢附上唯一標識符,通過該標識符找到Application Id
第一種思路實現起來簡單,但是不夠優雅,而且在併發查詢的情況下會是什麼效果沒有測試過.

第二種查詢由於Tez的原因,嘗試了很多種方案,都不能有效的設置查詢標識符.

該功能測試的通用連接代碼如下:

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

String url = "jdbc:hive2://<host>:10000/<database>";

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.confwhitelist和hive.security.authorization.sqlstd.confwhitelist.append.

我的配置最終如下:

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

但是mapred.job.name這個配置並不能生效. 類似的配置像hive.query.name,也沒有作用. Debug了一下,發現在HiveDriver層是能夠讀取並寫入conf的,而最終沒有生效,不能確定在Tez層發生了什麼.

從網上的資料來看,Tez層設置的Application Name是按照HIVE-<hive.session.id>來設置的. 這個配置項雖然不推薦修改,但是通過命令行提供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://<host>:10000/<database>";

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<String> applicationTypes = Sets.newHashSet();
applicationTypes.add("TEZ");

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

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

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

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

雖然client.getApplications()方法返回的是一個List,但是因爲我的Tag是唯一生成的,所以實際上只會返回一個Application Id

done!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章