HiveServer2在實際運用中的連線問題
首先宣告,這篇文章主要是分析解決HiveServer2在實際使用過程中遇到的一些連線方面的問題。
有些人可能對Hive與HiveServer2有些疑惑,Hive2其實就是針對Hive的一些不足的地方進行了重寫,所以這裡推薦使用Hive2來進行實際的應用開發。
Hadoop環境方面,我這裡是使用3臺物理以及CDH搭建的環境。關於CDH的搭建會另外寫一篇詳細的文章,CDH搭建步驟雖然繁瑣,但是搭建起來後使用還是挺方便的。
話不多說,直入正題。
- 在使用java的jdbc連線HiveServer2時出現瞭如下的報錯:
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.<init>(HiveConnection.java:168)
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.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.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連線池進行連線,執行一段後發現連線池報錯了(報錯內容忘了保留,大概意思是說連線已經關閉但是還在使用該連線,大家腦補一下,這裡主要講排查)。BP連線池的機制是先取出一定數目的連線保留在連線池,然後測試執行緒會隔一段時間去檢測連線是否可用,不可用會重新連線。按理說使用連線池時不會出現這樣的錯誤的,故而推測是不是服務端做了設定去定時的關閉一些沒有使用的連線?想到這立即開啟我們最愛的hive配置頁面搜尋了一下timeout這個關鍵字
果然找到了這麼幾項,這幾個配置的含義大家可以百度一下,分別是客戶端連線超時時間、連線檢查時間、空閒連線操作超時時間,空閒連線超時時間。(之前改過配置)主要關注下這個check的時間是15分鐘,也就是說它預設每隔15分鐘會檢查一次連線。而我們的BP連線池如果不做配置的話預設的檢查時間是4個小時,而預設的空閒時間是1個小時。服務端這邊的檢查時間比BP的檢查時間要快,也就是說有可能客戶端還未重連伺服器就把連線關閉了,這時連線池不知道連線時關閉的繼續拿出來用了。問題的原因時這樣,至於兩邊配置怎麼調整嘛大家自己權衡。其實你也可以選擇直接把服務端這邊的檢查關了。
以上就是本文的全部內容。hadoop這邊的配置項是非常非常多的,想要一項項研究顯然不現實,這些問題需要我們在實踐中不斷摸索前行。