[從零單排HBase 07]如何正確管理HBase的連接,從原理到實戰

本文將介紹HBase的客戶端連接實現,並說明如何正確管理HBase的連接。

最近在搭建一個HBase的可視化管理平臺,搭建完成後發現不管什麼查詢都很慢,甚至於使用api去listTable都要好幾秒。

經過一番排查發現,是每次請求的時候,都去臨時創建了一個connection,而創建connection非常耗時導致整體的rt上升。

因此,就深入瞭解了下如何正確管理HBase的connection,同時,也在優化過程中有些小細節的總結。

本文基於hbase 2.0.0版本的源碼,github上3.0版本的源碼已經有很大差異了,但是思想還是差不多的

1.HBase-client和HBase是如何連接的?

這個問題實際上在我之前的文章 深入HBase讀寫 中介紹過。

在這裏插入圖片描述
當HBase-client第一次請求讀寫的時候,需要三步走:

1)HBase-client從zk中獲取保存meta table的位置信息,知道meta table保存在了哪個region server,然後緩存這個位置信息;

2)HBase-client會查詢這個保存meta table的特定的region server,查詢meta table信息,在table中獲取自己想要訪問的row key所在的region在哪個region server上。

3)客戶端直接訪問目標region server,獲取對應的row

所以,我們知道hbase-client實際上包含三部分連接:

  • 跟zk連接,獲取相關元信息
  • 跟HMaster連接,做相關DDL操作
  • 直接跟各個region server進行連接,進行增刪改查

2.HBase客戶端連接原理

常規寫法是這樣的

Connection connection = ConnectionFactory.createConnection(conf);

try {
    Table table = connection.getTable(TableName.valueOf("tablename”));
    // 插入數據
    Put put = new Put(Bytes.toBytes("row"));
    put.addColumn(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), Bytes.toBytes("value"));
    table.put(put);
    // 單行讀取
    Get get = new Get(Bytes.toBytes("row"));
    Result res = table.get(get);
    // 刪除一行數據
    Delete delete = new Delete(Bytes.toBytes("row"));
    table.delete(delete);
}catch (IOException e) {
     //.....       
} finally {
    table.close();
    connection.close();     
}

我們不禁有這樣的疑問:

1)HBase沒有連接池嗎?

2)connection表示的是一個連接嗎?

3)connection每個線程都得創建嗎?線程安全嗎?

4)table每個線程都得創建嗎?線程安全嗎?

下面一一解答。

首先,Connection是線程安全的,而Table和Admin則不是線程安全的。

因此正確的做法是一個進程(或服務)使用一個Connection對象,而在不同的線程中使用單獨的Table和Admin對象。
Connection持有RpcClient,RpcClient管理了一個連接池poolMap

protected final PoolMap<ConnectionId, T> connections;

//….

this.connections = new PoolMap<>(getPoolType(conf), getPoolSize(conf));

通過AbstractRpcClient的getConnection看到,連接T繼承RpcConnection,叫做NettyRpcConnection。
在這裏插入圖片描述
這裏順便通過getPoolType和getPoolSize看了下線程池的大小和類型。

在枚舉類PoolType中有三種線程池類型Reusable, ThreadLocal, RoundRobin,用戶可以用hbase.client.ipc.pool.type指定線程池類型,通過hbase.client.ipc.pool.size指定線程池大小(默認是1)。

3.優化實踐

搞清楚上面的原理後,下面就可以開始優化我們的HBase管理平臺了。

只需要對每個HBase集羣的connection使用Map保存下來,每次請求的時候拿出對應的connection進去相關操作即可。然後需要注意在系統退出的時候關閉所有的connection。

上代碼:

public class ConnectionManager {
    private Map<String, Connection> connectionMap = new ConcurrentHashMap<>();

    public Connection getConnection(String resourceId, Configuration configuration) {
        ResourceInfo resourceInfo = ResourceInfoCache.getResourceInfoByCache(resourceId);
        if (resourceInfo == null) {
            throw new IllegalArgumentException("error resourceid: " + resourceId);
        }
        String key = getClusterKey(resourceInfo);
        if (connectionMap.containsKey(key)) {
            return connectionMap.get(key);
        }
        synchronized (this) {
        //做個DCL檢查
            if (connectionMap.containsKey(key)) {
                return connectionMap.get(key);
            }
            Connection connection = null;
            try {
                connection = ConnectionFactory.createConnection(configuration);
            } catch (IOException e) {
                return null;
            }
            connectionMap.put(key, connection);
            return connection;
        }
    }

    @PreDestroy
    public void doDestroy() {
        for (Map.Entry<String, Connection> entry : connectionMap.entrySet()) {
            Connection connection = entry.getValue();
            if (connection != null) {
               try {
                    connection.close();
                } catch (IOException e) {
                    //。。。。
                }
            }
        }
    }
}

這裏有幾個注意點:

  • 將ConnectionManager註冊爲bean,交給spring容器管理生命週期,同時保證單例。
  • 使用@PreDestroy保證應用關閉時,能正確釋放所有連接,避免連接泄漏
  • connectionMap使用ConcurrentHashMap保證線程安全
  • DCL檢查,避免重複創建同一個connection,浪費資源;同時避免重複創建connection後,無法關閉導致連接泄漏。

在需要查詢時,只需要通過getConnection獲取已經存在的connection即可。

當然,如果是普通的應用使用HBase-client,一般只需要對一個HBase的集羣創建全局唯一的一個Connection即可(一般交給spring容器管理),每次請求的時候,創建對應的Table進行CRUD。

看到這裏了,原創不易,點個贊吧,你最好看了~

知識碎片重新梳理,構建Java知識圖譜:https://github.com/saigu/JavaKnowledgeGraph (歷史文章查閱非常方便)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章