如何正確管理HBase的連線,從原理到實戰

阿丸發表於2020-04-19

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

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

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

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

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

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

這個問題實際上在我之前的文章 深入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。

如何正確管理HBase的連線,從原理到實戰

 

這裡順便通過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(歷史文章查閱非常方便)

掃碼關注我的公眾號“阿丸筆記”,第一時間獲取最新更新。同時可以免費獲取海量Java技術棧電子書、各個大廠面試題。

阿丸筆記

相關文章