之前我写了一个Spark的SQL Server,可以提供标准JDBC的连接方式去执行SQL,本质上是为了解决Spark Thrift Server带来的一系列问题,当时考虑到迁移成本,整个Server兼容了HiveServer2。
对于使用者来说,就是可以继续使用Hive的JDBC Driver,切换一个端口,就能提交Spark SQL。

但是由于引擎的约束,Hive的SQL Plan是发生在HS2,然后再启动MR作业,而Spark是提交到Yarn上后,在Driver进行SQL的计划生成,所以相对来说,连接HiveServer2的速度会快很多,因为HS2有自己的服务,而对于SparkSQL Server来说,则需要等待Yarn上的作业Ready了,才能收到反馈。

正常来说,这样的设计不会有啥问题,但是对于类似SQL网关服务来说,需要考虑超时机制,也就是假设连接太长,则需要返回timeout,但是对于Yarn来说,本身是不存在超时逻辑的,如果没有资源,就会一直等下去。

Hive的JDBC Driver是提供了超时的设置方式,具体的代码在:org/apache/hive/jdbc/HiveConnection.java 的构造器里面:

public HiveConnection(String uri, Properties info) throws SQLException {
setupLoginTimeout();
try {
connParams = Utils.parseURL(uri, info);
} catch (ZooKeeperHiveClientException e) {
throw new SQLException(e);
}
jdbcUriString = connParams.getJdbcUriString();

第一行的setupLoginTimeout();就设置了超时时间,setupLoginTimeout();的逻辑是:

// copy loginTimeout from driver manager. Thrift timeout needs to be in millis
private void setupLoginTimeout() {
long timeOut = TimeUnit.SECONDS.toMillis(DriverManager.getLoginTimeout());
if (timeOut > Integer.MAX_VALUE) {
loginTimeout = Integer.MAX_VALUE;
} else {
loginTimeout = (int) timeOut;
}
}

也就是说Hive的connection超时来自于DriverManager.getLoginTimeout(),而DriverManager.getLoginTimeout()是一个全局性的东西,这就导致一旦针对Hive单独设置了超时时间。

对于其他JDBC Driver比如Mysql,Presto都会受影响,Hive这个处理费方式很粗暴,正确的做法是应该透出一个接口支持单独针对Hive处理,社区在https://github.com/apache/hive/pull/1611 这个PR里面提供了一个解决方案。

基本的思路就是支持在JDBC连接的时候进行超时设置,例如这样:

Class.forName("org.apache.hive.jdbc.HiveDriver");
properties.put("socketTimeout", "60");
Connection connection = DriverManager.getConnection(JDBC_URL, properties);

目前的PR是合入了Master,在Hive4.0的release里面是有的,但是对于2x和3x就需要自己去cherry-pick了。


扫码手机观看或分享: