文章目录
前言
本文介绍了HRegion上Memstore flush的主体流程和主要细节。但是,HRegion只是HBase表中按照行的方向对一片连续的数据区域的抽象,它并不能对外提供单独的服务,供客户端或者HBase其它实体调用。而HRegion上MemStore的flush还是要通过HRegionServer来对外提供服务的。
如何初始化cacheFlusher
一.cacheFlusher
在HRegionServer中,有一个叫做cacheFlusher的东西,它是什么呢?我们先看一下它是如何被定义的:
// Cache flushing
// memstore内存刷新管理对象
protected MemStoreFlusher cacheFlusher;
发现,cacheFlusher是MemStoreFlusher类型的一个对象,我们来看下类的注释及定义:
/**
* Thread that flushes cache on request
* 处理刷新缓存请求的线程
*
* NOTE: This class extends Thread rather than Chore because the sleep time
* can be interrupted when there is something to do, rather than the Chore
* sleep time which is invariant.
*
* @see FlushRequester
*/
@InterfaceAudience.Private
class MemStoreFlusher implements FlushRequester {
cacheFlusher实际上就是HRegionServer上处理刷新缓存请求的线程。那么接下来的问题就是,cacheFlusher是如何被初始化的?它又是如何处理flush请求的?
首先,我们发现HRegionServer继承自HasThread,而HasThread实现了Runnable接口,那么在其内部肯定会执行run()方法,而run()方法的开始,有如下代码:
try {
// Do pre-registration initializations; zookeeper, lease threads, etc.
preRegistrationInitialization();
} catch (Throwable e) {
abort("Fatal exception during initialization", e);
}
继续往下看preRegistrationInitialization()方法,在其内部,调用了initializeThreads()方法,如下:
if (!isStopped() && !isAborted()) {
initializeThreads();
}
而这个initializeThreads()方法,做的主要工作就是初始化HRegionServer内部的各种工作线程,其中就包括cacheFlusher,代码如下:
// Cache flushing thread.
// 缓存刷新线程
this.cacheFlusher = new MemStoreFlusher(conf, this);
二.MemStoreFlusher类最主要的几个成员变量
然后我们在看这个MemStoreFlusher类是如何定义及工作的。首先看下它最主要的几个成员变量:
flushQueue
首先便是flushQueue,其定义如下:
private final BlockingQueue<FlushQueueEntry> flushQueue =
new DelayQueue<FlushQueueEntry>();
flushQueue是MemStoreFlusher中非常重要的一个变量,它是一个存储了Region刷新缓存请求的队列。而与flushQueue同时被更新的是regionsInQueue,它存储的是HRegion到FlushRegionEntry映射关系的集合,FlushRegionEntry是对发起memstore刷新请求的HRegion的一个封装,不仅包含了HRegion实例,还包括HRegion刷新memstore请求的产生时间,到期时间,以及一种类似续约的处理方式,即延长该请求的到期时间等。regionsInQueue的定义如下:
private final Map<HRegion, FlushRegionEntry> regionsInQueue =
new HashMap<HRegion, FlushRegionEntry>();
flushQueue和regionsInQueue的更新是同步的,即如果在flushQueue中加入或删除一条记录,那么在regionsInQueue中也会同步加入或删除一条记录。
flushHandlers
接下来便是flushHandlers,它是FlushHandler类型的一个数组,定义如下:
private final FlushHandler[] flushHandlers;
FlushHandler是处理缓存刷新的线程类,线程一旦启动后,在其run()方法内,就会不停的从flushQueue队列中拉取flush请求进行处理,其类的定义如下:
private class FlushHandler extends HasThread {
三.MemStoreFlusher的构造及成员变量的初始化
下面,我们来看下MemStoreFlusher的构造及成员变量的初始化,构造函数如下:
public MemStoreFlusher(final Configuration conf,
final HRegionServer server) {
super();
// 赋值RegionServer实例server
this.server = server;
// 线程唤醒频率threadWakeFrequency,取参数hbase.server.thread.wakefrequency配置的值,默认为10s,即线程的工作频率
this.threadWakeFrequency =
conf.getLong(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000);
// 获取最大可用堆内存max
long max = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax();
// 获取全局memstore所占堆内存的百分比globalMemStorePercent
float globalMemStorePercent = HeapMemorySizeUtil.getGlobalMemStorePercent(conf, true);
// 获取全局memstore大小限制值globalMemStoreLimit
this.globalMemStoreLimit = (long) (max * globalMemStorePercent);
// 获取全局memstore大小限制值的低水平线百分比globalMemStoreLimitLowMarkPercent
this.globalMemStoreLimitLowMarkPercent =
HeapMemorySizeUtil.getGlobalMemStoreLowerMark(conf, globalMemStorePercent);
// 获取全局memstore大小限制值的低水平线globalMemStoreLimitLowMark
this.globalMemStoreLimitLowMark =
(long) (this.globalMemStoreLimit * this.globalMemStoreLimitLowMarkPercent);
// 获取阻塞等待时间blockingWaitTime,取参数hbase.hstore.blockingWaitTime,默认为90000
this.blockingWaitTime = conf.getInt("hbase.hstore.blockingWaitTime",
90000);
// 获取flush处理线程数目handlerCount,取参数hbase.hstore.flusher.count,默认为2
int handlerCount = conf.getInt("hbase.hstore.flusher.count", 2);
// 构造handlerCount个flush处理线程数组,默认为2个,可通过hbase.hstore.flusher.count设置
this.flushHandlers = new FlushHandler[handlerCount];
// 记录日志信息
LOG.info("globalMemStoreLimit=" +
StringUtils.humanReadableInt(this.globalMemStoreLimit) +
", globalMemStoreLimitLowMark=" +
StringUtils.humanReadableInt(this.globalMemStoreLimitLowMark) +
", maxHeap=" + StringUtils.humanReadableInt(max));
}
MemStoreFlusher的构造函数比较简单,我们重点分析下获取全局memstore所占堆内存的百分比globalMemStorePercent的HeapMemorySizeUtil类的getGlobalMemStorePercent()方法,和获取全局memstore大小限制值的低水平线百分比globalMemStoreLimitLowMarkPercent的HeapMemorySizeUtil类的getGlobalMemStoreLowerMark()方法。
首先,看下获取全局memstore所占堆内存的百分比globalMemStorePercent的HeapMemorySizeUtil类的getGlobalMemStorePercent()方法,代码如下:
public static float getGlobalMemStorePercent(final Configuration c, final boolean logInvalid) {
// 获取全局memstore的大小,优先取参数hbase.regionserver.global.memstore.size,
// 未配置的话再取参数hbase.regionserver.global.memstore.upperLimit,
// 如果还未配置的话,默认为0.4
float limit = c.getFloat(MEMSTORE_SIZE_KEY,
c.getFloat(MEMSTORE_SIZE_OLD_KEY, DEFAULT_MEMSTORE_SIZE));
// 如果limit的值在区间(0,0.8]之外的话
if (limit > 0.8f || limit <= 0.0f) {
if (logInvalid) {// 根据参数logInvalid确定是否记录警告日志
LOG.warn("Setting global memstore limit to default of " + DEFAULT_MEMSTORE_SIZE
+ " because supplied value outside allowed range of (0 -> 0.8]");
}
// 将limit设置为0.4
limit = DEFAULT_MEMSTORE_SIZE;
}
// 返回limit
return limit;
}
这个方法的主要作用就是获取配置的全局memstore占整个heap内存的百分比。获取的逻辑如下:
1、获取配置的全局memstore占整个heap内存的百分比limit:优先取参数hbase.regionserver.global.memstore.size,未配置的话再取参数hbase.regionserver.global.memstore.upperLimit,如果还未配置的话,默认为0.4;
2、判断limit是否在区间(0,0.8]之外,根据参数logInvalid确定是否记录警告日志,并将limit设置为默认值0.4;
3、返回limit。
下面,我们再看下获取全局memstore大小限制值的低水平线百分比globalMemStoreLimitLowMarkPercent的HeapMemorySizeUtil类的getGlobalMemStoreLowerMark()方法,代码如下:
public static float getGlobalMemStoreLowerMark(final Configuration c, float globalMemStorePercent) {
// 取新参数hbase.regionserver.global.memstore.size.lower.limit
String lowMarkPercentStr = c.get(MEMSTORE_SIZE_LOWER_LIMIT_KEY);
// 如果新参数配置了的话,直接转化为double并返回
if (lowMarkPercentStr != null) {
return Float.parseFloat(lowMarkPercentStr);
}
// 取旧参数hbase.regionserver.global.memstore.lowerLimit"
String lowerWaterMarkOldValStr = c.get(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY);
// 如果旧参数配置的话,记录警告日志信息
if (lowerWaterMarkOldValStr != null) {
LOG.warn(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " is deprecated. Instead use "
+ MEMSTORE_SIZE_LOWER_LIMIT_KEY);
// 转化为double类型lowerWaterMarkOldVal
float lowerWaterMarkOldVal = Float.parseFloat(lowerWaterMarkOldValStr);
// 如果参数值大于计算得到的全局memstore所占堆内存的百分比,赋值为globalMemStorePercent,并记录日志信息
if (lowerWaterMarkOldVal > globalMemStorePercent) {
lowerWaterMarkOldVal = globalMemStorePercent;
LOG.info("Setting globalMemStoreLimitLowMark == globalMemStoreLimit " + "because supplied "
+ MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " was > " + MEMSTORE_SIZE_OLD_KEY);
}
// 返回lowerWaterMarkOldVal / globalMemStorePercent
return lowerWaterMarkOldVal / globalMemStorePercent;
}
// 如果新旧参数均未配置的话,默认为0.95
return DEFAULT_MEMSTORE_SIZE_LOWER_LIMIT;
}
这个方法的主要作用就是获取配置的全局memstore内存占全部heap内存的低水平线百分比。获取的逻辑如下:
1、取新参数hbase.regionserver.global.memstore.size.lower.limit配置的值,如果新参数配置了的话,直接转化为double并返回;
2、如果新参数未配置的话,取旧参数hbase.regionserver.global.memstore.lowerLimit配置的值,如果旧参数配置的话,记录警告日志信息,并:
2.1、将旧参数配置的值转化为double类型lowerWaterMarkOldVal;
2.2、如果旧参数值大于计算得到的全局memstore所占堆内存的百分比,赋值为globalMemStorePercent,并记录日志信息;
2.3、返回lowerWaterMarkOldVal / globalMemStorePercent;
3、如果新旧参数均未配置的话,默认为0.95。
总结
以上就是今天要讲的内容,本文简单介绍了如何初始化cacheFlusher,而如何处理flush请求的将在下文揭晓。