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. iOS工具种之16进制颜色转为UIColor

     #define DEFAULT_VOID_COLOR [UIColor whiteColor] + (UIColor *)colorWithHexString:(NSString *)stringT ...

  2. Eclipse中输入系统变量和运行参数--转

    原文地址:http://chenzhou123520.iteye.com/blog/1931670 在开发时,有时候可能需要根据不同的环境设置不同的系统参数,我们都知道,在使用java -jar命令时 ...

  3. 第四节:教你如何快速让浏览器兼容ES6特性

    写在正文前,本来这一节的内容应该放在第二节更合适,因为当时就有同学问ES6的兼容性如何,如何在浏览器兼容ES6的特性,这节前端君会介绍一个抱砖引玉的操作案例. 为什么ES6会有兼容性问题? 由于广大用 ...

  4. apk代码的破解

    方法一:dexdump方法(效果很不好,推荐指数*) 1.搜索到dexdump.exe所在目录: 2.将apk包中的**.dex文件存放到上面目录: 3.命令行中进入上面目录,执行:dexdump   ...

  5. Linux 下Mysql自动备份脚本

    backdb.sh 文件 #!/bin/bash USER="root" PASSWORD="888888" DATABASE="mydb" ...

  6. xfire找不到services.xml

    java.io.FileNotFoundException: class path resource [META-INF/xfire/services.xml] cannot be opened be ...

  7. 汇编程序w=x*y+z-200

    DATA   SEGMENTX      DW  1000Y      DW  2000Z      DW  3000W      DW  2 DUP(?)DATA   ENDSCODE   SEGM ...

  8. 【USACO 1.2.1】挤牛奶

    [问题描述] 三个农民每天清晨5点起床,然后去牛棚给3头牛挤奶.第一个农民在300时刻(从5点开始计时,秒为单位)给他的牛挤奶,一直到1000时刻.第二个农民在700时刻开始,在 1200时刻结束.第 ...

  9. Android之声音管理器《AudioManager》的使用以及音量控制

    以下为网上下载然后拼接-- Android声音管理AudioManager使用 手机都有声音模式,声音.静音还有震动,甚至震动加声音兼备,这些都是手机的基本功能.在Android手机中,我们同样可以通 ...

  10. 不熟悉的CSS样式

    1.font连写: 例如:font:15px/23px "ralewayregular","Helvetica Neue",Helvetica,Arial,sa ...