现象

hive-jdbc.2.1.1版本

在循环遍历查询数据,并且同时处理发现偶尔会出现SocketException异常,推测异常是由于超时导致的,结果验证果然超时时间被设置为了mysql的30s

Error retrieving next row
java.sql.SQLException: Error retrieving next row
        at org.apache.hive.jdbc.HiveQueryResultSet.next(HiveQueryResultSet.java:396)
        .....
Caused by: org.apache.thrift.transport.TTransportException: java.net.SocketException: Connection reset
        at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:129)
        at org.apache.thrift.transport.TTransport.readAll(TTransport.java:86)
        at org.apache.thrift.transport.TSaslTransport.readLength(TSaslTransport.java:376)
        at org.apache.thrift.transport.TSaslTransport.readFrame(TSaslTransport.java:453)

原因

  1. org.apache.hive.jdbc.HiveConnection的setupLoginTimeout方法可以看出超时时间默认取的是DriverManager.getLoginTimeout(),loginTimeout最终就是Socket的setSoTimeout时间
  
  private void setupLoginTimeout() {
    long timeOut = TimeUnit.SECONDS.toMillis(DriverManager.getLoginTimeout());
    if (timeOut > Integer.MAX_VALUE) {
      loginTimeout = Integer.MAX_VALUE;
    } else {
      loginTimeout = (int) timeOut;
    }
  }
    private TTransport createUnderlyingTransport() throws TTransportException {       
     ...
            transport = HiveAuthFactory.getSocketTransport(this.host, this.port, this.loginTimeout);
            ...
    }

// 下面是HiveAuthFactory类中的方法
  public static TTransport getSocketTransport(String host, int port, int loginTimeout) {
    return new TSocket(host, port, loginTimeout);
  }

// 下面是thrift的TSocket类中的方法
  private void initSocket() {
    socket_ = new Socket();
    try {
      socket_.setSoLinger(false, 0);
      socket_.setTcpNoDelay(true);
      socket_.setKeepAlive(true);
      socket_.setSoTimeout(socketTimeout_);
    } catch (SocketException sx) {
      LOGGER.error("Could not configure socket.", sx);
    }
  }
  1. 在DriverManager.setLoginTimeout方法上打断点调试可以看到DriverManager.getLoginTimeout()的超时时间被覆盖
    HikariDataSource.getConnection()---->new HikariPool(this)------>PoolBase.initializeDataSource()----->PoolBase.setLoginTimeout----->DriverDataSource.setLoginTimeout()----->DriverManager.setLoginTimeout

HikariDataSource extends HikariConfig

private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
 public HikariConfig()
   {
      connectionTimeout = CONNECTION_TIMEOUT;
   }

PoolBase.setLoginTimeout

   private void setLoginTimeout(final DataSource dataSource)
   {
      if (connectionTimeout != Integer.MAX_VALUE) {
         try {
            dataSource.setLoginTimeout(Math.max(1, (int) MILLISECONDS.toSeconds(500L + connectionTimeout)));
         }
         catch (Throwable e) {
            LOGGER.info("{} - Failed to set login timeout for data source. ({})", poolName, e.getMessage());
         }
      }
   }

Issue-HIVE-22196 有进行说明

修复方案

  • 重写org.apache.hive.jdbc.HiveConnection的setupLoginTimeout方法,打包上传到自己的maven仓库, 最新版本hive-jdbc-3.1.2中这个问题仍然存在
  • 在使用hive前手动调用 DriverManager.setLoginTimeout

Hive任务超时报错 Invalid OperationHandle

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐