Instead of creating an HTable instance for every request from your client application, it makes much more sense to create one initially and subsequently reuse them.
 
The primary reason for doing so is that creating an HTable instance is a fairly expensive operation that takes a few seconds to complete. In a highly contended environment with thousands of requests per second, you would not be able to use this approach at all—creating the HTable instance would be too slow. You need to create the instance
at startup and use it for the duration of your client’s life cycle.
 
There is an additional issue with the HTable being reused by multiple threads within the same process.
 
The HTable class is not thread-safe, that is, the local write buffer is not guarded against concurrent modifications. Even if you were to use setAutoFlush(true) (which is the default currently; see “Client-side write buffer” on page 86 ) this is not advisable. Instead, you should use one instance of HTable for each thread you are running in your client application.
 
综上所述,使用HTablePool的原因主要有两点:
(1)创建HTable实例是比较耗时的;
(2)HTable实例不是线程安全的。
 
在详细介绍HTablePool工作原理之前,需要弄明白它的两个依赖类PoolMap(直接依赖)、Pool(间接依赖)的实现方式。
 
Pool
Pool仅仅是一个接口,描述信息如下图所示:
 
该接口对应着三个实现类,实现方式均不同,如下图所示:
(1) ReusablePool
该类继承自ConcurrentLinkedQueue,顾名思义,池子内部使用队列(Queue)实现,获取资源里从队列头部摘取(poll),使用完成后再将资源放入(add)队列的尾部(不能超过池子配额限制),核心代码如下:
 
@Override
public R get() {
     return poll();
}
 
@Override
public R put(R resource) {
     if (size() < maxSize) {
          add(resource);
     }
     return null;
}
 
说明:资源池中的资源数量不会超过maxSize,但池子中的资源实例可能发生变化,当某一时刻池子中的资源已被耗尽(资源均处于外部使用状态,且尚未归还),这里如果有资源请求到来,get返回为Null,此时外部可能会立刻创建新的资源,但是和资源被释放归还的时刻是不定的,可能某一资源(不是新创建的资源)归还时发现资源池中的资源数量已达到配额限制,则该资源不能进入资源池(可能被丢弃回收),虽然资源实例发生变化,但不会影响资源池中的资源数目。
 
(2)RoundRobinPool
该类继承自CopyOnWriteArrayList,顾名思义,使用轮询方式访问列表中的资源,代码如下:
 
@Override
public R get() {
     if (size() < maxSize) {
          return null;
     }
     nextResource %= size();
     R resource = get(nextResource++);
     return resource;
}
 
资源池内部维护一个下标,获取资源时,下标对资源数量(size())求余并自增一,返回对应的资源(资源并没有被从资源池中移除)。使用轮询的方式可能会导致某一资源被外部(线程)同时使用,需要注意资源线程安全问题。
 
从上述代码也可以看出,当资源池中的资源数量小于配置限制时,资源池中的资源是不会被重用的。
 
资源回收的代码同上,在此不再赘述。
 
(3)ThreadLocalPool
该类继承自ThreadLocal,顾名思义,该资源池与线程有着紧密的联系,资源池中的资源数目取决于使用线程往资源池中添加资源的线程数目,每个线程仅可以使用属于自己线程的那个资源。
 
private static final Map<ThreadLocalPool<?>, AtomicInteger> poolSizes = new HashMap<ThreadLocalPool<?>, AtomicInteger>();
 
poolSizes 用来记录各个资源池实例中各自的资源数目(同一程序中可以有多个不同的资源池)。
 
资源的获取与归还主要利用ThreadLocal中的get、set方法,代码如下:
 
@Override
public R put(R resource) {
     R previousResource = get();
     if (previousResource == null) {
          AtomicInteger poolSize = poolSizes.get(this);
          if (poolSize == null) {
               poolSizes.put(this, poolSize = new AtomicInteger(0));
          }
          poolSize.incrementAndGet();
      }
      this.set(resource);
      return previousResource;
}
 
get、set中间的代码个人认为仅仅完成资源池审计功能。
 
PoolMap
 
HTablePool的资源池设计是以表名称(String、byte[])为单位的,即对HBase中的每一张表维护着各自的连接池,因此在Pool之上有了PoolMap,实际使用中PoolMap的Key即为表名称。
 
 
PoolMap内部维护着一个名为pools的变量,
 
private Map<K, Pool<V>> pools = new ConcurrentHashMap<K, Pool<V>>();
 
其中,Key为表名称,Value即为对应着的资源池实例。
 
请求资源代码如下:
 
@Override
public V get(Object key) {
     Pool<V> pool = pools.get(key);
     return pool != null ? pool.get() : null;
}
 
首先根据key(表名称)找到对应的资源池实例pool,然后从资源池实例pool中请求资源;
 
添加资源代码如下:
 
@Override
public V put(K key, V value) {
     Pool<V> pool = pools.get(key);
     if (pool == null) {
         pools.put(key, pool = createPool());
     }
     return pool != null ? pool.put(value) : null;
}
 
首先根据key(表名称)找到对应的资源池实例pool,然后通过该资源池实例pool添加资源,如果相应的资源池实例不存在,则创建并维护对应关系,创建代码如下:
 
protected Pool<V> createPool() {
     switch (poolType) {
     case Reusable:
          return new ReusablePool<V>(poolMaxSize);
     case RoundRobin:
          return new RoundRobinPool<V>(poolMaxSize);
     case ThreadLocal:
          return new ThreadLocalPool<V>();
     }
     return null;
}
 
根据poolType(ReusablePool、RoundRobinPool、ThreadLocalPool)创建相应类型的资源池。
 
在Pool、PoolMap的基础上,我们可以开始研究HTablePool的实现原理。
 
HTablePool
 
 
该类内部维护着两个重要变量:
 
private final PoolMap<String, HTableInterface> tables;
......
private final HTableInterfaceFactory tableFactory;
 
其中,tables维护着某表对应的连接资源(即HTable实例),tableFactory用以创建、释放HTable实例。
 
HTableInterfaceFactory拥有一个实例类HTableFactory,代码如下:
 
public class HTableFactory implements HTableInterfaceFactory {
     @Override
     public HTableInterface createHTableInterface(Configuration config,
         byte[] tableName) {
          try {
               return new HTable(config, tableName);
          } catch (IOException ioe) {
               throw new RuntimeException(ioe);
          }
     }
 
     @Override
     public void releaseHTableInterface(HTableInterface table) throws IOException {
          table.close();
     }
}
 
HTableFactory工作过程比较简单,创建、释放(关闭)HTable实例。
 
请求某表连接资源(HTable实例)代码如下:
 
public HTableInterface getTable(String tableName) {
     // call the old getTable implementation renamed to findOrCreateTable
     HTableInterface table = findOrCreateTable(tableName);
     // return a proxy table so when user closes the proxy, the actual table
     // will be returned to the pool
     return new PooledHTable(table);
}
 
private HTableInterface findOrCreateTable(String tableName) {
     HTableInterface table = tables.get(tableName);
     if (table == null) {
          table = createHTable(tableName);
     }
     return table;
}
 
protected HTableInterface createHTable(String tableName) {
     return this.tableFactory.createHTableInterface(config,Bytes.toBytes(tableName));
}
 
(1)根据表名称查询或创建HTableInterface(HTable实现该接口)实例,由方法findOrCreateTable完成;
(2)tables中含有该表名称对应的实例,则直接返回,否则通过方法createHTable创建(即通过tableFactory创建)后返回;
(3)使用代理模式对HTableInterface实例进行包装,将包装后的实例(即PooledHTable实例)返回给调用者。
 
PooledHTable通过代理模式将请求全部发送至内部HTable实例,但有一个方法请求例外,即close方法,
 
class PooledHTable implements HTableInterface {
 
    private HTableInterface table; // actual table implementation
 
    public PooledHTable(HTableInterface table) {
      this.table = table;
    }
 
    /**
     * Returns the actual table back to the pool
     * 
     * @throws IOException
     */
    public void close() throws IOException {
      returnTable(table);
    }
 
    @Override
    public RowLock lockRow(byte[] row) throws IOException {
      return table.lockRow(row);
    }
 
}
 
该方法不再通过HTableInterface close方法执行关闭操作,而是将实例table返回至相应的资源池中,代码如下:
 
private void returnTable(HTableInterface table) throws IOException {
     // this is the old putTable method renamed and made private
     String tableName = Bytes.toString(table.getTableName());
     if (tables.size(tableName) >= maxSize) {
          // release table instance since we're not reusing it
          this.tables.remove(tableName, table);
          this.tableFactory.releaseHTableInterface(table);
          return;
     }
     tables.put(tableName, table);
}
 
如果相应的表名称的资源池中的资源数目已经达到配额限制,则将该资源从资源池中移除(某些资源池中实现并没有将资源实际移除资源池,参考前面分析),否则根据对应关系将资源归还即可。
 
 
 
 
 

HBase HTablePool的更多相关文章

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

      说明: 最近两天在调研HBase的连接池,有了一些收获,特此记录下来. 本文先将官方文档(http://hbase.apache.org/book.html)9.3.1.1节翻译,方便大家阅读,然 ...

  2. Java 向Hbase表插入数据报(org.apache.hadoop.hbase.client.HTablePool$PooledHTable cannot be cast to org.apac)

    org.apache.hadoop.hbase.client.HTablePool$PooledHTable cannot be cast to org.apac 代码: //1.create HTa ...

  3. 【甘道夫】HBase连接池 -- HTablePool是Deprecated之后

    说明: 近期两天在调研HBase的连接池,有了一些收获,特此记录下来. 本文先将官方文档(http://hbase.apache.org/book.html)9.3.1.1节翻译,方便大家阅读,然后查 ...

  4. Java 向Hbase表插入数据报(org.apache.hadoop.hbase.client.HTablePool$PooledHTable cannot be cast to org.apac

    org.apache.hadoop.hbase.client.HTablePool$PooledHTable cannot be cast to org.apac 代码: //1.create HTa ...

  5. HBase概念学习(九)HTablePool为何弃用?

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/jiq408694711/article/details/36526433 转载请注明出处:jiq•钦 ...

  6. Java 向Hbase表插入数据异常org.apache.hadoop.hbase.client.HTablePool$PooledHTable cannot be cast to org.apache.client.HTable

    出错代码如下: //1.create HTablePool HTablePool hp=new HTablePool(con, 1000); //2.get HTable from HTablepoo ...

  7. hbase scan 的例子

    /** * Created by han on 2016/1/28. */ import org.apache.hadoop.conf.Configuration; import org.apache ...

  8. HBase Java简单示例

    Hbase采用Java实现,原生客户端也是Java实现,其他语言需要通过thritf接口服务间接访问Hbase的数据. Hbase作为大数据存储数据库,其写能力非常强,加上Hbase本身就脱胎于Had ...

  9. 【hbase】——Java操作Hbase进行建表、删表以及对数据进行增删改查,条件查询

    1.搭建环境 新建JAVA项目,添加的包有: 有关Hadoop的hadoop-core-0.20.204.0.jar 有关Hbase的hbase-0.90.4.jar.hbase-0.90.4-tes ...

随机推荐

  1. 【C语言天天练(二一)】内联函数

            引言:调用函数时,一般会由于建立调用.传递參数.跳转到函数代码并返回等花费掉一些时间,C语言的解决的方法是使用类函数宏.在C99中,还提出了第二种方法:内联函数.         内联 ...

  2. How to load jars residing over NFS in JBossAS7 classpath ? --reference

    In this article we will discuss how can we load jars residing over NFS in JBoss AS-7 classpath. In s ...

  3. [转] 设计模式 -- Facade

    PS:Façade模式注重简化接口,Adapter模式注重转换接口,Bridge模式注重分离接口(抽象)与其实现,Decorator模式注重稳定接口的前提下为对象扩展功能. Facade模式 Faca ...

  4. Android(java)学习笔记215:多线程断点下载的原理(JavaSE实现)

    1. 为什么需要多线程下载?     服务器的资源有限,同时的平均地分配给每个客户端.开启的线程越多抢占的服务的资源就越多,下载的速度就越块. 2. 下载速度的限制条件? (1)你的电脑手机宽带的带宽 ...

  5. Java基础知识强化17:JAVA不可以将所覆盖的方法的访问权限变得比父类的小

    首先我们看一下下面的代码如下: interface I { void go(); } abstract class A implements I { } class C extends A { voi ...

  6. Sublime Text3使用详解

    Sublime Text简介 Sublime Text - 性感的代码编辑器.程序员之必备神器 Sublime Text 是一个代码编辑器,也是HTML和散文先进的文本编辑器.Sublime Text ...

  7. redisbook笔记——redis内部数据结构

    在Redis的内部,数据结构类型值由高效的数据结构和算法进行支持,并且在Redis自身的构建当中,也大量用到了这些数据结构. 这一部分将对Redis内存所使用的数据结构和算法进行介绍. 动态字符串 S ...

  8. C#接口的使用

    .接口: 接口与抽象类一样,也是表示某种规则,一旦使用了该规则,就必须实现相关的方法.对于C#语言而言,由于只能继承自一个父类,因此若有多个规则需要实现,则使用接口是个比较好的做法. .接口的定义 i ...

  9. js 去掉浏览器右击默认事件

    1.整个页面所有的右击事件 document.oncontextmenu = function(){ return false; } 2.特定的区域 document.getElementById(& ...

  10. android 9Path图片的使用

    Android UI设计时,经常会使用图片作为背景,比如给按钮设置背景图片时,图片会默认缩放来适应整个按钮.但是有时这种缩放效果并不是我们所需求的.而我们只是希望缩放图片的特定位置,以此来保证按钮的视 ...