hbase源码学习.Lease
引用:
http://hi.baidu.com/xuelianglv/blog/item/43adb1103504ef07203f2e0c.html#0
在BigTable的论文中讲到了Lease的概念。Least就好比你租房子住,签了多长的合约。如果时间长了,你可以续约(renew)。你也可能因为房子不习惯想换个地住,你就和房东说,取消住房(cancel)。当然在Hbase里,你不用交违约金。
因为BigTable会同时处理很多个客户端,就好比是一个有多套房子的房东同时把不同房间租给了多个不同的客户。那它关心什么呢?我想他首先关心的是每
个客户的合约(lease)什么时候到期了。在HBase里,也有类似的问题需要进行管理。每个合约用一个Lease表示,这个类继承了java的
Delayed接口。Delayed接口简单的讲,就是告诉其它人在过一段时间要有事发生.
在这里,用于Lease合约超期了通知它人。这个它人是在创建Lease时指定的,就好比是签租房合同时上面填写的租房人名字。
总结下,这里提到了几个概念:
合约 (租房合约 ): Least
它人 (合约过期通知对象): Listener
在Hbase里,每一份Lease都用一个唯一的名字来标识它。如果你想renew一个不存在的Lease或者想创建一个已经存在的Lease都会抛出异常。
最后,HBase具体怎么判断哪个Lease过期了呢?是由Leases类实现的。它把所有的Lease存放在DelayQueue中,并不断轮询是否由Lease
过期了。轮询(poll)的频率由leaseCheckFrequency控制。Lease的租用时间长度由leasePeriod指定。
lease = leaseQueue.poll(leaseCheckFrequency, TimeUnit.MILLISECONDS);
当轮询到一个Lease过期了,就将该Lease删除并通知该Lease的Listener。
-----------------------------------------------------------------------------------------------------------
我的学习收获:
-----------------------------------------------------------------------------------------------------------
总的来说HBase中的lease使用很简单:
1)创建对象实例时,指明所有lease过期时间和check过期操作的频率;
2)创建lease,指明lease的name和lease过期时的操作,即一个concrete的LeaseListener。
稍微关心一下其内部实现。“每个合约用一个Lease表示,这个类继承了java的 Delayed接口”。Delayed接口中包含了两方面的信息:1)delay time;2)如何进行Compare。对于1),是为了把lease存放在DelayQueue中,从而能按照指定的delay time将lease从队列中poll出来;对于2),是为了保证在DelayQueue中的lease按照delay time由小到大的顺序排队。实际上DelayQueue内部使用了PriorityQueue,并且没有指定Comparator,所以就是按照队列中对象实现的Comparable进行由小到大顺序排列。
Leases的结构:
可以看出,Lease开始run()之后就会不断从DelayQueue中poll出Lease,一旦获得Lease(该lease当然是已经过期了)就调用lease.getListerner().leaseExpired().
HBase中有两个concrete的LeaseListener:RowLockListener和ScannerListener。
Case 1:
在对HBase中一行进行操作时HBase会对这一行加锁,一般情况下这个锁会unlock,但是在regionserver非常繁忙的时候(比如split过多)或者出现异常时这个锁的释放无法执行。此时就要依靠lease来释放这个锁:
public void leaseExpired() {
LOG.info("Row Lock " + this.lockName + " lease expired");
Integer r = rowlocks.remove(this.lockName);
if (r != null) {
region.releaseRowLock(r);
}
}
Case 2:
在使用HTable进行scan操作的时候用户常常会忘记调用ScanResult.close()。此时也需要lease来释放资源:
public void leaseExpired() {
LOG.info("Scanner " + this.scannerName + " lease expired");
RegionScanner s = scanners.remove(this.scannerName);
if (s != null) {
try {
HRegion region = getRegion(s.getRegionInfo().getRegionName());
if (region != null && region.getCoprocessorHost() != null) {
region.getCoprocessorHost().preScannerClose(s);
}
s.close();
if (region != null && region.getCoprocessorHost() != null) {
region.getCoprocessorHost().postScannerClose(s);
}
} catch (IOException e) {
LOG.error("Closing scanner for "
+ s.getRegionInfo().getRegionNameAsString(), e);
}
}
}
}