通过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://<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.confwhitelisthive.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 NameDefault ValueDescription
tez.queue.namenullString 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 NameDefault ValueDescription
tez.application.tagnullString 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

总结

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

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
你可以使用Java代码提交Hive SQL到YARN上运行,以下是一个示例: ```java import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.LocalResource; import org.apache.hadoop.yarn.api.records.LocalResourceType; import org.apache.hadoop.yarn.api.records.LocalResourceVisibility; import org.apache.hadoop.yarn.client.api.YarnClient; import org.apache.hadoop.yarn.client.api.YarnClientApplication; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.util.Apps; import org.apache.hadoop.yarn.util.ConverterUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class SubmitHiveSqlToYarn { public static void main(String[] args) throws Exception { // 初始化YARN配置 Configuration conf = new YarnConfiguration(); // 创建YARN客户端 YarnClient yarnClient = YarnClient.createYarnClient(); yarnClient.init(conf); yarnClient.start(); // 创建YARN应用 YarnClientApplication app = yarnClient.createApplication(); // 设置应用名称 String appName = "Hive SQL on YARN"; app.setApplicationName(appName); // 创建容器启动命令 String command = "hive -e \"SELECT * FROM my_table\""; // 设置本地资源 Map<String, LocalResource> localResources = new HashMap<>(); File scriptFile = new File("path/to/hive_script.hql"); FileInputStream scriptFileStream = new FileInputStream(scriptFile); LocalResource scriptFileResource = createLocalResource(scriptFileStream, conf); localResources.put("hive_script.hql", scriptFileResource); // 设置容器环境变量 Map<String, String> env = new HashMap<>(); env.put("CLASSPATH", "./*"); // 设置容器资源 Map<String, String> containerResources = new HashMap<>(); containerResources.put("hive_script.hql", scriptFile.getAbsolutePath()); // 设置容器启动命令 Apps.addToEnvironment(env, "HADOOP_USER_NAME", UserGroupInformation.getCurrentUser().getShortUserName()); Apps.addToEnvironment(env, "HADOOP_HOME", System.getenv("HADOOP_HOME")); Apps.addToEnvironment(env, "HADOOP_CONF_DIR", System.getenv("HADOOP_CONF_DIR")); String command = "hive -f hive_script.hql"; containerResources.put("script", scriptFile.getAbsolutePath()); // 设置应用主类 String mainClass = "org.apache.hadoop.yarn.applications.distributedshell.ApplicationMaster"; // 设置应用主类参数 String[] mainArgs = new String[]{ "--jar", "path/to/your/jar", "--shell_command", command, "--shell_env", "CLASSPATH=./*", "--shell_env", "HADOOP_USER_NAME=" + UserGroupInformation.getCurrentUser().getShortUserName(), "--shell_env", "HADOOP_HOME=" + System.getenv("HADOOP_HOME"), "--shell_env", "HADOOP_CONF_DIR=" + System.getenv("HADOOP_CONF_DIR"), "--container_memory", "1024", "--num_containers", "1" }; // 提交应用 ApplicationId appId = app.getNewApplicationResponse().getApplicationId(); Apps.addToEnvironment(env, "APP_ID", appId.toString()); Apps.addToEnvironment(env, "SCRIPT_NAME", "hive_script.hql"); Apps.addToEnvironment(env, "CONTAINER_RESOURCES", containerResources.toString()); Apps.addToEnvironment(env, "CONTAINER_LAUNCH_COMMAND", command); Apps.addToEnvironment(env, "CONTAINER_ENV", env.toString()); Apps.addToEnvironment(env, "MAIN_CLASS", mainClass); Apps.addToEnvironment(env, "MAIN_ARGS", mainArgs.toString()); Apps.addToEnvironment(env, "LOCAL_RESOURCES", localResources.toString()); Apps.addToEnvironment(env, "APP_NAME", appName); // 创建应用主类 YarnClientApplication clientApplication = yarnClient.createApplication(); ApplicationSubmissionContext appContext = clientApplication.getApplicationSubmissionContext(); appContext.setApplicationName(appName); // 设置资源需求 ResourceRequest resourceRequest = ResourceRequest.newInstance( Priority.newInstance(0), ResourceRequest.ANY, Resource.newInstance(1024, 1), 1 ); appContext.setAMContainerResourceRequest(resourceRequest); // 设置应用主类信息 ContainerLaunchContext amContainer = ContainerLaunchContext.newInstance( localResources, env, Collections.singletonList(command), null, null, null ); appContext.setAMContainerSpec(amContainer); // 提交应用 yarnClient.submitApplication(appContext); } private static LocalResource createLocalResource(FileInputStream fileInputStream, Configuration configuration) throws IOException { // 创建本地资源 LocalResource localResource = Records.newRecord(LocalResource.class); // 设置本地资源类型 localResource.setType(LocalResourceType.FILE); // 设置本地资源可见性 localResource.setVisibility(LocalResourceVisibility.APPLICATION); // 设置本地资源大小 FileStatus fileStatus = FileSystem.get(configuration).getFileStatus(new Path("path/to/hive_script.hql")); localResource.setSize(fileStatus.getLen()); // 设置本地资源时间戳 localResource.setTimestamp(fileStatus.getModificationTime()); // 设置本地资源位置 Path path = new Path("path/to/hive_script.hql"); URL url = ConverterUtils.getYarnUrlFromURI(path.toUri()); localResource.setResource(url); return localResource; } } ``` 上述代码中,主要步骤如下: 1. 初始化YARN配置 2. 创建YARN客户端 3. 创建YARN应用 4. 设置应用名称 5. 创建容器启动命令 6. 设置本地资源 7. 设置容器环境变量 8. 设置容器资源 9. 设置容器启动命令 10. 设置应用主类 11. 设置应用主类参数 12. 提交应用 这个示例中,我们使用了`hive -f`命令来执行Hive SQL脚本,你需要将`my_table`替换为你的表名,将`path/to/hive_script.hql`替换为你的Hive SQL脚本所在的路径,将`path/to/your/jar`替换为你的Java程序的jar包路径。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值