Hbase的连接池--HTablePool被Deprecated之后

说明:

最近两天在调研HBase的连接池,有了一些收获,特此记录下来。

本文先将官方文档(http://hbase.apache.org/book.html)9.3.1.1节翻译,方便大家阅读,然后查阅了关键类HConnectionManager的Developer API(http://hbase.apache.org/devapidocs/index.html) 做了一些总结。

最后介绍一些阅读0.96、0.98及最新源码的精彩发现。

1.连接

HTable是HBase的client,负责从meta表中找到目标数据所在的RegionServers,当定位到目标RegionServers后,client直接和RegionServers交互,而不比再经过master。

HTable实例并不是线程安全的。如果有过多的线程尝试与单个HTable实例通信,那么写缓冲器可能会崩溃,这是可以使用HTablePool类进行操作。

当需要创建HTable实例时,明智的做法是使用相同的HBaseConfiguration实例,这使得共享连接到RegionServers的ZK和socket实例,例如,应该使用这样的代码:

HBaseConfiguration conf = HBaseConfiguration.create();
HTable table1 = new HTable(conf, "myTable");
HTable table2 = new HTable(conf, "myTable");
而不是这样的代码:
HBaseConfiguration conf1 = HBaseConfiguration.create();
HTable table1 = new HTable(conf1, "myTable");
HBaseConfiguration conf2 = HBaseConfiguration.create();
HTable table2 = new HTable(conf2, "myTable");

2.连接池

当面对多线程访问需求时,我们可以预先建立HConnection,参见以下代码:

Example 9.1. Pre-Creating a HConnection

// Create a connection to the cluster.
HConnection connection = HConnectionManager.createConnection(Configuration);
HTableInterface table = connection.getTable("myTable");
// use table as needed, the table returned is lightweight
table.close();
// use the connection for other access to the cluster
connection.close();

构建HTableInterface实现是非常轻量级的,并且资源是可控的。

注意:

HTablePool是HBase连接池的老用法,该类在0.94,0.95和0.96中已经不建议使用,在0.98.1版本以后已经移除。

3.HConnectionManager

该类是连接池的关键,专门介绍。

HConnectionManager是一个不可实例化的类,专门用于创建HConnection。

最简单的创建HConnection实例的方式是HConnectionManager.createConnection(config),该方法创建了一个连接到集群的HConnection实例,该实例被创建的程序管理。通过这个HConnection实例,可以使用HConnection.getTable(byte[])方法取得HTableInterface implementations的实现,

例如:

 HConnection connection = HConnectionManager.createConnection(config);
            HTableInterface table = connection.getTable("tablename");
            try {
                // Use the table as needed, for a single operation and a single thread
            } finally {
                table.close();
                connection.close();
            }  

3.1构造函数

无,不可实例化。

3.2常用方法

(1)static HConnection  createConnection(org.apache.hadoop.conf.Configuration conf)创建一个新的HConnection实例。

该方法绕过了常规的HConnection生命周期管理,常规是通过getConnection(Configuration)来获取连接。调用方负责执行Closeable.close()来关闭获得的连接实例。

推荐的创建HConnection的方法是:

HConnection connection = HConnectionManager.createConnection(conf);

HTableInterface table = connection.getTable("mytable");

table.get(...);

...

table.close();

connection.close();

(2)public static HConnection getConnection(org.apache.hadoop.conf.Configuration conf)

根据conf获取连接实例。如果没有对应的连接实例存在,该方法创建一个新的连接。

注意:该方法在0.96和0.98版本中都被Deprecated了,不建议使用,但是在最新的未发布代码版本中又复活了!!!

3.3实例代码

package com.bigdata.dbhbase096;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HConnection;
import org.apache.hadoop.hbase.client.HConnectionManager;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;

public class ConnectionPoolTest {
    private static final String QUORUM = "compute1";
    private static final String CLIENTPORT = "2181";
    private static final String TABLENAME = "minifiletable4";
    private static Configuration conf = null;
    private static HConnection conn = null;

    static{
        try {
            conf =  HBaseConfiguration.create();
            conf.set("hbase.zookeeper.quorum", QUORUM);
            conf.set("hbase.zookeeper.property.clientPort", CLIENTPORT);
            conn = HConnectionManager.createConnection(conf);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }    

    public static void main(String[] args) throws IOException {
        HTableInterface htable = ConnectionPoolTest.conn.getTable(TABLENAME);
        try {
            Scan scan = new Scan();
            ResultScanner rs = htable.getScanner(scan);
            for (Result r : rs.next(5)) {
                for (Cell cell : r.rawCells()) {
                    System.out.println("Rowkey : " + Bytes.toString(r.getRow())
                            + "   Familiy:Quilifier : "
                            + Bytes.toString(CellUtil.cloneQualifier(cell))
                            + "   Value : "
                            + Bytes.toString(CellUtil.cloneValue(cell))
                            + "   Time : " + cell.getTimestamp());
                }
            }
        } finally {
            htable.close();
        }

    }
}

4.阅读源码的新发现

4.1消失的HConnectionManager.getConnection

  从0.96和0.98版本HConnectionManager的源码中可以看到

static final Map<HConnectionKey, HConnectionImplementation> CONNECTION_INSTANCES;

就是连接池,连接池中的每个连接用HConnectionKey来标识,然而,HConnectionManager源码中所有涉及CONNECTION_INSTANCES的方法全都被Deprcated了。

我们来看已经被Deprecated的getConnection方法:

/**
   * Get the connection that goes with the passed <code>conf</code> configuration instance.
   * If no current connection exists, method creates a new connection and keys it using
   * connection-specific properties from the passed {@link Configuration}; see
   * {@link HConnectionKey}.
   * @param conf configuration
   * @return HConnection object for <code>conf</code>
   * @throws ZooKeeperConnectionException
   */
  @Deprecated
  public static HConnection getConnection(final Configuration conf)
  throws IOException {
    HConnectionKey connectionKey = new HConnectionKey(conf);
    synchronized (CONNECTION_INSTANCES) {
      HConnectionImplementation connection = CONNECTION_INSTANCES.get(connectionKey);
      if (connection == null) {
        connection = (HConnectionImplementation)createConnection(conf, true);
        CONNECTION_INSTANCES.put(connectionKey, connection);
      } else if (connection.isClosed()) {
        HConnectionManager.deleteConnection(connectionKey, true);
        connection = (HConnectionImplementation)createConnection(conf, true);
        CONNECTION_INSTANCES.put(connectionKey, connection);
      }
      connection.incCount();
      return connection;
    }
  }  

根据传入的conf构建HConnectionKey,然后以HConnectionKey实例为key到连接池Map对象CONNECTION_INSTANCES中去查找connection,如果找到就返回connection,如果找不到就新建,如果找到但已被关闭,就删除再新建

我们来看HConnectionKey的构造函数:

HConnectionKey(Configuration conf) {
    Map<String, String> m = new HashMap<String, String>();
    if (conf != null) {
      for (String property : CONNECTION_PROPERTIES) {
        String value = conf.get(property);
        if (value != null) {
          m.put(property, value);
        }
      }
    }
    this.properties = Collections.unmodifiableMap(m);
    try {
      UserProvider provider = UserProvider.instantiate(conf);
      User currentUser = provider.getCurrent();
      if (currentUser != null) {
        username = currentUser.getName();
      }
    } catch (IOException ioe) {
      HConnectionManager.LOG.warn("Error obtaining current user, skipping username in HConnectionKey", ioe);
    }
  }  

由以上源码可知,接收conf构造HConnectionKey实例时,其实是将conf配置文件中的属性赋值给HConnectionKey自身的属性,换句话说,不管你new几次,只要conf的属性相同,new出来的HConnectionKey实例的属性都相同。

结论一:conf的属性 --》 HConnectionKey实例的属性

接下来,回到getConnection源码中看到这样一句话:

 HConnectionImplementation connection = CONNECTION_INSTANCES.get(connectionKey);

该代码是以HConnectionKey实例为key来查找CONNECTION_INSTANCES这个LinkedHashMap中是否已经包含了HConnectionKey实例为key的键值对,这里要注意的是,map的get方法,其实获取的是key的hashcode,这个自己读JDK源码就能看到。

而HConnectionKey已经重载了hashcode方法:

 @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    if (username != null) {
      result = username.hashCode();
    }
    for (String property : CONNECTION_PROPERTIES) {
      String value = properties.get(property);
      if (value != null) {
        result = prime * result + value.hashCode();
      }
    }
    return result;
  }  

在该代码中,最终返回的hashcode取决于当前用户名及当前conf配置文件的属性。所以,只要conf配置文件的属性和用户相同,HConnectionKey实例的hashcode就相同!

结论二:conf的属性 --》HConnectionKey实例的hashcode

再来看刚才这句代码:

HConnectionImplementation connection = CONNECTION_INSTANCES.get(connectionKey);

对于get方法的参数connectionKey,不管connectionKey是不是同一个对象,只要connectionKey的属性相同,那connectionKey的hasecode就相同,对于get方法而言,也就是同样的key!!!

所以,可以得出结论三:conf的属性 --》HConnectionKey实例的hashcode --》 get返回的connection实例

结论三换句话说说:

conf的属性相同 --》 CONNECTION_INSTANCES.get返回同一个connection实例

然而,假设我们的HBase集群只有一个,那我们的HBase集群的conf配置文件也就只有一个(固定的一组属性),除非你有多个HBase集群另当别论。

在这样一个机制下,如果只有一个conf配置文件,则连接池中永远只会有一个connection实例!那“池”的意义就不大了!

所以,代码中才将基于getConnection获取池中物的机制Deprecated了,转而在官方文档中建议:

*******************************************************************************************************************

当面对多线程访问需求时,我们可以预先建立HConnection,参见以下代码:

Example 9.1. Pre-Creating a HConnection

// Create a connection to the cluster.
HConnection connection = HConnectionManager.createConnection(Configuration);
HTableInterface table = connection.getTable("myTable");
// use table as needed, the table returned is lightweight
table.close();
// use the connection for other access to the cluster
connection.close();

构建HTableInterface实现是非常轻量级的,并且资源是可控的。

*******************************************************************************************************************

如果大家按照官方文档的建议做了,也就是预先创建了一个连接,以后的访问都共享该连接,这样的效果其实和过去的getConnection完全一样,都是在玩一个connection实例

4.2 HBase的新时代

我查看了Git上最新版本的代码(https://git-wip-us.apache.org/repos/asf?p=hbase.git;a=tree),发现getConnection复活了:

/**
   * Get the connection that goes with the passed <code>conf</code> configuration instance.
   * If no current connection exists, method creates a new connection and keys it using
   * connection-specific properties from the passed {@link Configuration}; see
   * {@link HConnectionKey}.
   * @param conf configuration
   * @return HConnection object for <code>conf</code>
   * @throws ZooKeeperConnectionException
   */
  public static HConnection getConnection(final Configuration conf) throws IOException {
      return ConnectionManager.getConnectionInternal(conf);
  }  

这个不是重点,重点是最新版本代码的pom:

  <groupId>org.apache.hbase</groupId>

40   <artifactId>hbase</artifactId>

   <packaging>pom</packaging>

 <version>2.0.0-SNAPSHOT</version>

  <name>HBase</name>

   <description>

     Apache HBase\99 is the Hadoop database. Use it when you need

     random, realtime read/write access to your Big Data.

    This project‘s goal is the hosting of very large tables -- billions of rows X millions of columns -- atop clusters

     of commodity hardware.

  </description>

HBase即将迎来2.0.0版本!!

HBase的下一个发布版是否会像Hadoop2.0那样来一个华丽丽的升华,迎来众多牛逼的新特性呢?

CHANGES.txt文档中没法得到最新的信息,最后一次更新还在2012年2月24日,看来开源大佬们也是爱编码不爱写文档的主。。

参考:http://blog.csdn.net/u010967382/article/details/38046821

时间: 2024-08-29 11:37:18

Hbase的连接池--HTablePool被Deprecated之后的相关文章

【甘道夫】HBase连接池 -- HTablePool被Deprecated之后

说明: 最近两天在调研HBase的连接池,有了一些收获,特此记录下来. 本文先将官方文档(http://hbase.apache.org/book.html)9.3.1.1节翻译,方便大家阅读,然后查阅了关键类HConnectionManager的Developer API(http://hbase.apache.org/devapidocs/index.html) 做了一些总结. 最后介绍一些阅读0.96.0.98及最新源码的精彩发现. 欢迎转载,请注明来源: http://blog.csdn

Http请求连接池 - HttpClient 的 PoolingHttpClientConnectionManager

两个主机建立连接的过程是非常复杂的一个过程,涉及到多个数据包的交换,而且也非常耗时间.Http连接须要的三次握手开销非常大,这一开销对于比較小的http消息来说更大.但是假设我们直接使用已经建立好的http连接.这样花费就比較小.吞吐率更大. 传统的HttpURLConnection并不支持连接池.假设要实现连接池的机制,还须要自己来管理连接对象.对于网络请求这种底层相对复杂的操作.个人以为假设有可用的其它方案,也没有必要自己去管理连接对象. 除了HttpURLConnection,大家肯定还知

Spring Boot1.5.4 连接池 和 事务

原文:https://github.com/x113773/testall/issues/10 默认连接池---spring Boot中默认支持的连接池有Tomcat.HikariCP .DBCP .DBCP2,以下摘自[官方文档](http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-configure-datasource):> Production database

FineBI学习系列之FineBI与HBase数据连接(图文详解)

不多说,直接上干货! 这是来自FineBI官网提供的帮助文档 http://help.finebi.com/http://help.finebi.com/doc-view-584.html 目录: 1.描述 2.操作 3.注意事项 1.描述 Hbase作为大数据平台的重要组成部分,在数据存储方面起到了至关重要的作用,因此BI连接Hbase也是必不可少的功能之一.FineBI提供的方法是通过phoenix连接hbase,下面我们将详细介绍如何进行连接. 2.操作 2.1 配置信息   驱动 URL

oracle database resident connection pooling(驻留连接池)

oracle在11g中引入了database resident connection pooling(DRCP).在此之前,我们可以使用dedicated 或者share 方式来链接数据库,dedicated方式是oracle数据库默认的链接方式,无需过多的配置,而且关于dedicated的bug也是非常少的,因此,通常情况下,建议使用dedicated方式来链接数据库.但是,在服务器资源有限,并且同时连接数据库的用户量非常大时,dedicated方式就无能为力了.假设并发用户为5000,每个d

缓冲池,线程池,连接池

SSH:[email protected]:unbelievableme/object-pool.git   HTTPS:https://github.com/unbelievableme/object-pool.git 缓冲池 设计要点:包含三个队列:空缓冲队列(emq),装满输入数据的输入的队列(inq),装满输出数据的输出队列(outq),输入程序包括收容输入(hin),提取输入(sin),输出程序包括收容输出(hout)和提取输出(sout). 注意点:输入程序和输出程序会对缓冲区并发访

DBCP连接池使用问题

问题现象: 启动应用,访问无压力,一切正常,一段时间过后,应用访问异常. 问题分析: 1.web容器线程爆满,拒绝服务.由于应用并发量大,线程响应时间长增加,线程池连接数逐步递增直到爆满,导致应用拒绝服务. 2.通过对线程信息的分析,发现线程处理时间都卡在连接数据库中,通过对数据库服务器的检查,数据库是没有问题的. 3.通过查询服务器日志,发现数据库连接异常:连接超时. 4.查询DBCP连接池连接使用情况,空闲链接和使用链接还正常. 问题思考: 从以上信息可以确认,问题一定出在应用层,并且是应用

一种利用ADO连接池操作MySQL的解决方案(VC++)

VC++连接MySQL数据库 常用的方式有三种:ADO.mysql++,mysql API ; 本文只讲述ADO的连接方式. 为什么要使用连接池? 对于简单的数据库应用,完全可以先创建一个常连接(此连接永远不关闭,直接数进程退出),但是这样做至少会引起两个问题:(1)资源竞争,多个数据库请求操作不能同时进行,后一请求必须要等到前一请求完成后才能进行:(2)多线程情况下容易出现混乱,甚至出现资源异常释放.还有一种方法,就是使用数据库时创建连接,使用完后关闭连接回收资源.这种方式在数据库操作频繁的情

mongodb or操作与连接池

mongodb # 类似于sql中的in或者or操作 mulites field query: db.cool.find({$or:[{field1:'val'},{'field2':'val'}-]}) # 类似于sql中的like操作 db.coo.find('name': /m/) == sql like pymongo 使用 {'field':{$regex: keyword}} http://stackoverflow.com/questions/3305561/how-do-i-qu