【甘道夫】HBase连接池,甘道夫hbase


说明: 最近两天在调研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.net/u010967382/article/details/38046821

1.连接 HTable是HBase的client,负责从meta表中找到目标数据所在的RegionServers,当定位到目标RegionServers后,client直接和RegionServers交互,而不比再经过master。 HTable实例并不是线程安全的。当需要创建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版本以后已经移除。
BTW:简陋的官方文档到此为止。。。。Orz
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 fulong.bigdata.hbase; 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 = "FBI001,FBI002,FBI003";     private static final String CLIENTPORT = "2181";     private static final String TABLENAME = "rd_ns:itable";     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: 39   <groupId>org.apache.hbase</groupId> 40   <artifactId>hbase</artifactId> 41   <packaging>pom</packaging> 42   <version>2.0.0-SNAPSHOT</version> 43   <name>HBase</name> 44   <description> 45     Apache HBase\99 is the Hadoop database. Use it when you need 46     random, realtime read/write access to your Big Data. 47     This project's goal is the hosting of very large tables -- billions of rows X millions of columns -- atop clusters 48     of commodity hardware. 49   </description>
HBase即将迎来2.0.0版本!! HBase的下一个发布版是否会像Hadoop2.0那样来一个华丽丽的升华,迎来众多牛逼的新特性呢? 从CHANGES.txt文档中没法得到最新的信息,最后一次更新还在2012年2月24日,看来开源大佬们也是爱编码不爱写文档的主。。

hbase,为何存储速度快

1 ,了解hbase么,为什么存储速度快?
2理解spring么,它的AOP实现是基于什么原理,bean的初始化过程是那些(涉及具体的源代码),在bean factory初始化前 ,运行中,初始化后想做些事情。该怎么做?
3 struts1和strus2的区别,strust2关于 ThredLocal是什么,strus2初始化部分的原理是什么?
4多线程用过么?
5关于jvm,它是什么样的结构?,他的Gc算法是什么,什么情况下会 out of memory?
6设计模式了解多少,工厂模式中的抽象工厂和简单工厂模式用于那些场景,你怎么理解策略模式,怎样应用的? 策略模式和状态模式的本质区别,状态模式和命令模式的区别?
7 连接池原理,连接池commit 后连接是否关闭?

1) HBASE存储速度快吗???请问hbase和谁比较,来说明存储速度快?是在同等量级较差的MYSQL架构还是和mongodb/cassandra比较?
HBASE的存储速度并不快,原因太多了.
2) bean factory初始化过程??这个问题是你问错了,还是我TMD的不会啊??我只知道bean factory初始化bean的时候,可以使用factoryBean接口/InitializingBean接口,以及在bean配置文件中指定init初始化方法等来"补充"初始化过程...
3) struts这烂货技术,我就不再罗嗦,对于threadLocal这个玩意,是spring/struts中框架中最常用的"基于线程内"对象引用管理的绝B手段,任何一个Thread实例(请参看Thread API)都有一个threadLocalMap属性,此属性即保持了当前thread中所有的threadLocal对象.ThreadLocal给我们提供了访问和传递"用户实现级别线程私有变量"的极佳的手段..
4) 多线程用户吗??MLGB的,这问题问的,老子经常用.面试官同学,你用过多线程吗??你会死在多线程中,知道不??问点具体的行不??你用过java不??
你用过电脑不??
5) JVM的结构是什么??请问你是想知道JVM的内存模型??这个,我可以不告诉吗?你猜!!!
GC算法是什么??这个嘛,回答思路:对象标记算法 ---->GC种类 -->GC算法.
什么时候会OOM?好多可能的奥,最直观的说旧时代或者持久带放不下了呗,无论是minor GC还是full GC还是创建对象都无法在数据重分配时满足内存要求时,不过还有奥,就是你通过"直接内存分配"时OS也满足不了你的时候,也会OOM奥..那你就讲讲minor GC和full GC的过程呗..

6) 设计模式嘛,这个玩意,目前在整个软件工程中,就是个幽灵,说白了也算是个操蛋的理论....我不再赘述.
7) 连接池,是一个广义的概念,广泛存在于所有基于IO操作的server架构中.覆盖了多种数据存储服务/客户端数据存取服务/基于server的web服务模式下.我们的tomcat,mysql,mongodb,甚至我们的基于OS的各种驱动器等等等等,fuck太多了....你是想问mysql吗??
commit是提交数据变更(无论是显式的还是隐式的),它和关闭连接由关系吗??TMD的各种客户端驱动程序实现都有,你到底想问那种实现呢??或者我问,为什么要关闭连接呢??我TMD的com......余下全文>>
 


相关内容