首先声明,这篇文章主要是分析解决HiveServer2在实际使用过程中遇到的一些连接方面的问题。
有些人可能对Hive与HiveServer2有些疑惑,Hive2其实就是针对Hive的一些不足的地方进行了重写,所以这里推荐使用Hive2来进行实际的应用开发。
Hadoop环境方面,我这里是使用3台物理以及CDH搭建的环境。关于CDH的搭建会另外写一篇详细的文章,CDH搭建步骤虽然繁琐,但是搭建起来后使用还是挺方便的。
话不多说,直入正题。
2018-04-09 23:58:54,163 ERROR [RMSLThread.java:249]
java.sql.SQLException: Could not open client transport with JDBC Uri: jdbc:hive2://39.137.3.11:10000/default: java.net.SocketException: Connection reset
at org.apache.hive.jdbc.HiveConnection.openTransport(HiveConnection.java:231)
at org.apache.hive.jdbc.HiveConnection.
at org.apache.hive.jdbc.HiveDriver.connect(HiveDriver.java:105)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:247)
at com.fonsview.tools.HiveUtil.getConn(HiveUtil.java:23)
at com.fonsview.action.RMSLThread.con2Hive(RMSLThread.java:212)
at com.fonsview.action.RMSLThread.run(RMSLThread.java:66)
at java.lang.Thread.run(Thread.java:748)
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.receiveSaslMessage(TSaslTransport.java:178)
at org.apache.thrift.transport.TSaslTransport.open(TSaslTransport.java:307)
at org.apache.thrift.transport.TSaslClientTransport.open(TSaslClientTransport.java:38)
at org.apache.hive.jdbc.HiveConnection.openTransport(HiveConnection.java:203)
... 8 more
Caused by: java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:210)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:127)
... 13 more
Connection reset这种错误常见的情况有两种:第一个就是如果一端的Socket被关闭(或主动关闭或者因为异常退出而引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常。另一个是一端退出,但退出时并未关闭该连接,另一端如果在从连接中读数据则抛出该异常(Connection reset)。
这里写一个测试代码进行测试:
public class Main {
private static String driverName = "org.apache.hive.jdbc.HiveDriver";
private static String url = "jdbc:hive2://172.16.0.12:10000/default";
private static String user = "root";
private static String password = "hello123";
private static String sql = "";
private static ResultSet res;
private static BoneCP connectionPool;
// static {
// try {
// Class.forName(driverName);
// BoneCPConfig config = new BoneCPConfig();
// config.setJdbcUrl(url);
// config.setUsername(user);
// config.setPassword(password);
// config.setMinConnectionsPerPartition(3);
// config.setMaxConnectionsPerPartition(6);
// config.setPartitionCount(1);// 设置分区个数
// config.setConnectionTimeoutInMs(5000);
// connectionPool = new BoneCP(config);
// } catch (Exception e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// private static final Logger log = Logger.getLogger(HiveJdbcCli.class);
public static void main(String[] args) {
Connection conn = null;
while (true) {
Statement stmt = null;
try {
// if (conn.isClosed()) {
// System.out.println("reconnect...");
conn = getConn();
// }
stmt = conn.createStatement();
showTables(stmt);
} catch (ClassNotFoundException e) {
System.out.println(new Date());
e.printStackTrace();
} catch (SQLException e) {
System.out.println(new Date());
e.printStackTrace();
} finally {
try {
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static void showTables(Statement stmt) throws SQLException {
sql = "show tables";
System.out.println("Running:" + sql);
res = stmt.executeQuery(sql);
System.out.println("执行 show tables 运行结果:");
while (res.next()) {
System.out.println(res.getString(1));
}
String sql2 = "show databases";
ResultSet res2 = stmt.executeQuery(sql2);
System.out.println("执行 show databses 运行结果:");
while (res2.next()) {
System.out.println(res2.getString(1));
}
}
private static Connection getConn() throws ClassNotFoundException,
SQLException {
// return connectionPool.getConnection();
Class.forName(driverName);
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
}
不断的连接hive2执行sql语句,运行后发现过一段时间就会报上述错误。为了排查问题,登录到CDH的hive界面
在这里发现了一些端倪,注意这个total number of sessions:100,这个表示的是连接hive2的总的进程数。100没有再增长,推测可能有相关配置限制这个连接数。查看hive2的配置
果不其然,找到了这个配置。上面的hive2页面可以看到哪些ip连接到了hive2,仔细观察发现除了正在跑的测试程序,之前在物理机上的应用也在连接hive2。这时我在想应该是由于应用的哪个地方处理完后未关闭连接从而导致连接被占满。这里修改代码将finally块中的关闭连接代码去掉,运行一段时间后出现了同样的报错,且连接数也为100。
找到问题后查看应用的代码确实是有个地方使用后忘了关闭连接,修改后应该就正常了。
注:这里还需要注意的一点就是Statement的关闭要在Connection的前面,如果位置颠倒的话会报错。
为了提高效率项目替换了原本的连接方式改用BP连接池进行连接,运行一段后发现连接池报错了(报错内容忘了保留,大概意思是说连接已经关闭但是还在使用该连接,大家脑补一下,这里主要讲排查)。BP连接池的机制是先取出一定数目的连接保留在连接池,然后测试线程会隔一段时间去检测连接是否可用,不可用会重新连接。按理说使用连接池时不会出现这样的错误的,故而推测是不是服务端做了设置去定时的关闭一些没有使用的连接?想到这立即打开我们最爱的hive配置页面搜索了一下timeout这个关键字
果然找到了这么几项,这几个配置的含义大家可以百度一下,分别是客户端连接超时时间、连接检查时间、空闲连接操作超时时间,空闲连接超时时间。(之前改过配置)主要关注下这个check的时间是15分钟,也就是说它默认每隔15分钟会检查一次连接。而我们的BP连接池如果不做配置的话默认的检查时间是4个小时,而默认的空闲时间是1个小时。服务端这边的检查时间比BP的检查时间要快,也就是说有可能客户端还未重连服务器就把连接关闭了,这时连接池不知道连接时关闭的继续拿出来用了。问题的原因时这样,至于两边配置怎么调整嘛大家自己权衡。其实你也可以选择直接把服务端这边的检查关了。
以上就是本文的全部内容。hadoop这边的配置项是非常非常多的,想要一项项研究显然不现实,这些问题需要我们在实践中不断摸索前行。