《HBase源代码分析之HRegion上MemStore的flsuh流程(一)》《HBase源代码分析之HRegion上MemStore的flsuh流程(二)》等文中。我们介绍了HRegion上Memstore
flush的主体流程和主要细节。

可是,HRegion仅仅是HBase表中依照行的方向对一片连续的数据区域的抽象,它并不能对外提供单独的服务,供client或者HBase其他实体调用。而HRegion上MemStore的flush还是要通过HRegionServer来对外提供服务的。以下,我们就具体探究下HRegionServer上是怎样实现这点的。

在HRegionServer中。有一个叫做cacheFlusher的东东。它是什么呢?我们先看一下它是怎样被定义的:

  1. // Cache flushing
  2. // memstore内存刷新管理对象
  3. protected MemStoreFlusher cacheFlusher;

能够发现,cacheFlusher是MemStoreFlusher类型的一个对象,我们来看下类的凝视及定义:

  1. /**
  2. * Thread that flushes cache on request
  3. * 处理刷新缓存请求的线程
  4. *
  5. * NOTE: This class extends Thread rather than Chore because the sleep time
  6. * can be interrupted when there is something to do, rather than the Chore
  7. * sleep time which is invariant.
  8. *
  9. * @see FlushRequester
  10. */
  11. @InterfaceAudience.Private
  12. class MemStoreFlusher implements FlushRequester {

cacheFlusher实际上就是HRegionServer上处理刷新缓存请求的线程。那么接下来的问题就是,cacheFlusher是怎样被初始化的?它又是怎样处理flush请求的?带着这两个问题,我们继续本文。

一、怎样初始化cacheFlusher

首先,我们发现HRegionServer继承自HasThread,而HasThread实现了Runnable接口,那么在其内部肯定会运行run()方法,而run()方法的開始,有例如以下代码:

  1. try {
  2. // Do pre-registration initializations; zookeeper, lease threads, etc.
  3. preRegistrationInitialization();
  4. } catch (Throwable e) {
  5. abort("Fatal exception during initialization", e);
  6. }

继续追踪preRegistrationInitialization()方法,在其内部。调用了initializeThreads()方法,例如以下:

  1. if (!isStopped() && !isAborted()) {
  2. initializeThreads();
  3. }

而这个initializeThreads()方法,做的主要工作就是初始化HRegionServer内部的各种工作线程。当中就包含cacheFlusher,代码例如以下:

  1. // Cache flushing thread.
  2. // 缓存刷新线程
  3. this.cacheFlusher = new MemStoreFlusher(conf, this);

接下来。我们在看看这个MemStoreFlusher类是怎样定义及工作的。首先看下它最基本的几个成员变量:

首当其冲的便是flushQueue。其定义例如以下:

  1. private final BlockingQueue<FlushQueueEntry> flushQueue =
  2. new DelayQueue<FlushQueueEntry>();

flushQueue是MemStoreFlusher中很重要的一个变量,它是一个存储了Region刷新缓存请求的队列。

而与flushQueue同一时候被更新的是regionsInQueue,它存储的是HRegion到FlushRegionEntry映射关系的集合,FlushRegionEntry是对发起memstore刷新请求的HRegion的一个封装。不仅包括了HRegion实例,还包括HRegion刷新memstore请求的产生时间,到期时间,以及一种类似续约的处理方式,即延长该请求的到期时间等。regionsInQueue的定义例如以下:

  1. private final Map<HRegion, FlushRegionEntry> regionsInQueue =
  2. new HashMap<HRegion, FlushRegionEntry>();

flushQueue和regionsInQueue的更新是同步的,即假设在flushQueue中增加或删除一条记录,那么在regionsInQueue中也会同步增加或删除一条记录。

接下来比較重要的便是flushHandlers。它是FlushHandler类型的一个数组,定义例如以下:

  1. private final FlushHandler[] flushHandlers;

FlushHandler是什么呢?它是处理缓存刷新的线程类,线程一旦启动后,在其run()方法内,就会不停的从flushQueue队列中拉取flush请求进行处理。其类的定义例如以下:

  1. /**
  2. * 处理缓存刷新的线程类
  3. */
  4. private class FlushHandler extends HasThread {

以上就是MemStoreFlusher内运行flush流程最重要的几个成员变量,其它的变量都是一些辅助性的,这里不再做具体介绍。

以下,我们来看下MemStoreFlusher的构造及成员变量的初始化,构造函数例如以下:

  1. /**
  2. * @param conf
  3. * @param server
  4. */
  5. public MemStoreFlusher(final Configuration conf,
  6. final HRegionServer server) {
  7. super();
  8.  
  9. // 赋值RegionServer实例server
  10. this.server = server;
  11.  
  12. // 线程唤醒频率threadWakeFrequency,取參数hbase.server.thread.wakefrequency配置的值。默觉得10s,即线程的工作频率
  13. this.threadWakeFrequency =
  14. conf.getLong(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000);
  15.  
  16. // 获取最大可用堆内存max
  17. long max = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax();
  18.  
  19. // 获取全局memstore所占堆内存的百分比globalMemStorePercent
  20. float globalMemStorePercent = HeapMemorySizeUtil.getGlobalMemStorePercent(conf, true);
  21.  
  22. // 获取全局memstore限制大小值globalMemStoreLimit
  23. this.globalMemStoreLimit = (long) (max * globalMemStorePercent);
  24.  
  25. // 获取全局memstore限制大小值的低水平线百分比globalMemStoreLimitLowMarkPercent
  26. this.globalMemStoreLimitLowMarkPercent =
  27. HeapMemorySizeUtil.getGlobalMemStoreLowerMark(conf, globalMemStorePercent);
  28.  
  29. // 获取全局memstore限制大小值的低水平线globalMemStoreLimitLowMark
  30. this.globalMemStoreLimitLowMark =
  31. (long) (this.globalMemStoreLimit * this.globalMemStoreLimitLowMarkPercent);
  32.  
  33. // 获取堵塞等待时间blockingWaitTime,取參数hbase.hstore.blockingWaitTime,默觉得90000
  34. this.blockingWaitTime = conf.getInt("hbase.hstore.blockingWaitTime",
  35. 90000);
  36.  
  37. // 获取flush处理线程数目handlerCount,取參数hbase.hstore.flusher.count,默觉得2
  38. int handlerCount = conf.getInt("hbase.hstore.flusher.count", 2);
  39.  
  40. // 构造handlerCount个flush处理线程数组。默觉得2个。可通过hbase.hstore.flusher.count设置
  41. this.flushHandlers = new FlushHandler[handlerCount];
  42.  
  43. // 记录日志信息
  44. LOG.info("globalMemStoreLimit=" +
  45. StringUtils.humanReadableInt(this.globalMemStoreLimit) +
  46. ", globalMemStoreLimitLowMark=" +
  47. StringUtils.humanReadableInt(this.globalMemStoreLimitLowMark) +
  48. ", maxHeap=" + StringUtils.humanReadableInt(max));
  49. }

MemStoreFlusher的构造函数比較简单。我们重点分析下获取全局memstore所占堆内存的百分比globalMemStorePercent的HeapMemorySizeUtil类的getGlobalMemStorePercent()方法。和获取全局memstore限制大小值的低水平线百分比globalMemStoreLimitLowMarkPercent的HeapMemorySizeUtil类的getGlobalMemStoreLowerMark()方法。

首先,看下获取全局memstore所占堆内存的百分比globalMemStorePercent的HeapMemorySizeUtil类的getGlobalMemStorePercent()方法,代码例如以下:

  1. /**
  2. * Retrieve global memstore configured size as percentage of total heap.
  3. * 获取配置的全局memstore占整个heap内存的百分比
  4. * @param c
  5. * @param logInvalid
  6. */
  7. public static float getGlobalMemStorePercent(final Configuration c, final boolean logInvalid) {
  8.  
  9. // 获取全局memstore的大小。优先取參数hbase.regionserver.global.memstore.size,
  10. // 未配置的话再取參数hbase.regionserver.global.memstore.upperLimit,
  11. // 假设还未配置的话,默觉得0.4
  12. float limit = c.getFloat(MEMSTORE_SIZE_KEY,
  13. c.getFloat(MEMSTORE_SIZE_OLD_KEY, DEFAULT_MEMSTORE_SIZE));
  14.  
  15. // 假设limit的值在区间(0,0.8]之外的话
  16. if (limit > 0.8f || limit <= 0.0f) {
  17. if (logInvalid) {// 依据參数logInvalid确定是否记录警告日志
  18. LOG.warn("Setting global memstore limit to default of " + DEFAULT_MEMSTORE_SIZE
  19. + " because supplied value outside allowed range of (0 -> 0.8]");
  20. }
  21.  
  22. // 将limit设置为0.4
  23. limit = DEFAULT_MEMSTORE_SIZE;
  24. }
  25.  
  26. // 返回limit
  27. return limit;
  28. }

这种方法的主要作用就是获取配置的全局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()方法,代码例如以下:

  1. /**
  2. * Retrieve configured size for global memstore lower water mark as percentage of total heap.
  3. * 获取配置的全局memstore内存占所有heap内存的低水平线百分比
  4. * @param c
  5. * @param globalMemStorePercent
  6. */
  7. public static float getGlobalMemStoreLowerMark(final Configuration c, float globalMemStorePercent) {
  8.  
  9. // 取新參数hbase.regionserver.global.memstore.size.lower.limit
  10. String lowMarkPercentStr = c.get(MEMSTORE_SIZE_LOWER_LIMIT_KEY);
  11.  
  12. // 假设新參数配置了的话。直接转化为double并返回
  13. if (lowMarkPercentStr != null) {
  14. return Float.parseFloat(lowMarkPercentStr);
  15. }
  16.  
  17. // 取旧參数hbase.regionserver.global.memstore.lowerLimit"
  18. String lowerWaterMarkOldValStr = c.get(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY);
  19.  
  20. // 假设旧參数配置的话,记录警告日志信息
  21. if (lowerWaterMarkOldValStr != null) {
  22. LOG.warn(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " is deprecated. Instead use "
  23. + MEMSTORE_SIZE_LOWER_LIMIT_KEY);
  24.  
  25. // 转化为double类型lowerWaterMarkOldVal
  26. float lowerWaterMarkOldVal = Float.parseFloat(lowerWaterMarkOldValStr);
  27.  
  28. // 假设參数值大于计算得到的全局memstore所占堆内存的百分比,赋值为globalMemStorePercent。并记录日志信息
  29. if (lowerWaterMarkOldVal > globalMemStorePercent) {
  30. lowerWaterMarkOldVal = globalMemStorePercent;
  31. LOG.info("Setting globalMemStoreLimitLowMark == globalMemStoreLimit " + "because supplied "
  32. + MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " was > " + MEMSTORE_SIZE_OLD_KEY);
  33. }
  34.  
  35. // 返回lowerWaterMarkOldVal / globalMemStorePercent
  36. return lowerWaterMarkOldVal / globalMemStorePercent;
  37. }
  38.  
  39. // 假设新旧參数均未配置的话,默觉得0.95
  40. return DEFAULT_MEMSTORE_SIZE_LOWER_LIMIT;
  41. }

这种方法的主要作用就是获取配置的全局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请求

通过怎样初始化cacheFlusher部分的介绍,我们已经知道,在MemStoreFlusher内部,存在两个存储flush请求及其HRegion封装类的队列和集合。即flushQueue和regionsInQueue,而MemStoreFlusher对外提供了一个requestFlush()方法。我们大体看下这种方法:

  1. /**
  2. * 请求刷新,
  3. * 即将须要刷新MemStore的HRegion放置到regionsInQueue中,
  4. * 同一时候依据HRegion构造FlushRegionEntry实例。加入到flushQueue中
  5. */
  6. public void requestFlush(HRegion r) {
  7. synchronized (regionsInQueue) {// 使用synchronizedkeyword对regionsInQueue进行线程同步
  8.  
  9. if (!regionsInQueue.containsKey(r)) {// 假设regionsInQueue中不存在相应HRegion
  10.  
  11. // This entry has no delay so it will be added at the top of the flush
  12. // queue. It'll come out near immediately.
  13. // 将HRegion类型的r封装成FlushRegionEntry类型的fqe
  14. // 这个fqe没有delay,即延迟运行时间,所以它被加入到flush队列的顶部。不久它将出列被处理。
  15. FlushRegionEntry fqe = new FlushRegionEntry(r);
  16.  
  17. // 将HRegion->FlushRegionEntry的相应关系加入到regionsInQueue集合
  18. // 将flush请求FlushRegionEntry加入到flushQueue队列
  19. // 从这里能够看出regionsInQueue、flushQueue这两个成员变量go together
  20. this.regionsInQueue.put(r, fqe);
  21. this.flushQueue.add(fqe);
  22. }
  23. }
  24. }

requestFlush()方法的主要作用,就是加入一个flush region的请求至MemStoreFlusher内部队列。其主要逻辑例如以下:

1、首先须要使用synchronizedkeyword对regionsInQueue进行线程同步,这么做是为了防止多线程的并发。

2、然后推断regionsInQueue中是否存在相应的HRegion,假设regionsInQueue集合中不存在相应HRegion的话继续,否则直接返回;

3、既然regionsInQueue集合中不存在相应HRegion,将HRegion类型的r封装成FlushRegionEntry类型的fqe;

4、将HRegion->FlushRegionEntry的相应关系加入到regionsInQueue集合;

5、将flush请求FlushRegionEntry加入到flushQueue队列。

从上述4、5步就能够看出regionsInQueue、flushQueue这两个成员变量go together。而且这个fqe没有delay,即延迟运行时间,所以它被加入到flush队列的顶部。不久它将出列被处理。

这个该怎么理解呢?我们还是回到flushQueue的定义,flushQueue是一个存储了Region刷新缓存请求的队列,里面存储的是实现了FlushQueueEntry接口的对象。FlushQueueEntry未定义不论什么行为。可是继承了java.util.concurrent.Delayed接口,故flushQueue是java中的DelayQueue,队列里存储的对象有一个过期时间的概念。

既然flush的请求已经被加入至flushQueue队列,相当于生产者已经把产品生产出来了,那么谁来消费呢?这个消费者的角色就是由FlushHandler线程来担任的。既然是线程,那么处理的逻辑肯定在其run()方法内,可是在研究其run()方法前,我们先看下flushQueue中存储的都是什么东西?

我们再回想下flushQueue的定义。它是一个存储了FlushQueueEntry的队列DelayQueue。我们先看下FlushQueueEntry的定义:

  1. interface FlushQueueEntry extends Delayed {
  2. }

一个集成了java的Delayed接口的无不论什么方法的空接口而已,那么它都有哪些实现类呢?答案就是WakeupFlushThread和FlushRegionEntry。在介绍这二者之前。我们首先介绍下flushQueue相应的队列类型---Java中的DelayQueue。

众所周知,DelayQueue是一个无界的BlockingQueue,其内部存储的必定是实现了Delayed接口的对象。所以,FlushQueueEntry必须实现java的Delayed接口。

而这样的队列中的成员有一个最大特点,就是仅仅有在其到期后才干出列,而且该队列内的成员都是有序的。从头至尾依照延迟到期时间的长短来排序。

那么怎样推断成员是否到期呢?相应成员对象的getDelay()方法返回一个小于等于0的值,就说明相应对象在队列中已到期,能够被取走。

既然DelayQueue中存储的成员对象都是有序的,那么实现了Delayed接口的类,必须提供compareTo()方法。用以排序。而且须要实现上述getDelay()方法,推断队内成员是否到期能够被取走。

接下来,我们分别来研究下WakeupFlushThread和FlushRegionEntry。

首先。WakeupFlushThread很easy,没有不论什么实质内容,代码例如以下:

  1. /**
  2. * Token to insert into the flush queue that ensures that the flusher does not sleep
  3. * 增加到刷新队列的确保刷新器不睡眠的令牌
  4. */
  5. static class WakeupFlushThread implements FlushQueueEntry {
  6. @Override
  7. public long getDelay(TimeUnit unit) {
  8. return 0;
  9. }
  10.  
  11. @Override
  12. public int compareTo(Delayed o) {
  13. return -1;
  14. }
  15.  
  16. @Override
  17. public boolean equals(Object obj) {
  18. return (this == obj);
  19. }
  20. }

它的主要作用是做为一个占位符或令牌插入到刷新队列flushQueue,以确保FlushHandler不会休眠。并且,其getDelay()方法返回值为0,说明其不存在延迟时间。入列后就可以出列。而它的compareTo()方法返回的值是-1。说明它与其他WakeupFlushThread在队内的顺序是等价的,无前后之分。实际上WakeupFlushThread区分前后也没有意义,它本身也没有实质性的内容。

接下来。我们再看下FlushRegionEntry类,其定义例如以下:

  1. /**
  2. * Datastructure used in the flush queue. Holds region and retry count.
  3. * Keeps tabs on how old this object is. Implements {@link Delayed}. On
  4. * construction, the delay is zero. When added to a delay queue, we'll come
  5. * out near immediately. Call {@link #requeue(long)} passing delay in
  6. * milliseconds before readding to delay queue if you want it to stay there
  7. * a while.
  8. *
  9. * 用在刷新队列里的数据结构。
  10.  
  11. 保存region和重试次数。
  12.  
  13. * 跟踪对象多大(ps.即时间)
  14. * 实现了java的Delayed接口。
  15. * 在构造方法里。delay为0。
  16. * 假设你想要它在队列中保持在在被又一次增加delay队列之前
  17. *
  18. *
  19. */
  20. static class FlushRegionEntry implements FlushQueueEntry {
  21.  
  22. // 待flush的HRegion
  23. private final HRegion region;
  24.  
  25. // 创建时间
  26. private final long createTime;
  27.  
  28. // 何时到期
  29. private long whenToExpire;
  30.  
  31. // 重入队列次数
  32. private int requeueCount = 0;
  33.  
  34. FlushRegionEntry(final HRegion r) {
  35.  
  36. // 待flush的HRegion
  37. this.region = r;
  38.  
  39. // 创建时间为当前时间
  40. this.createTime = EnvironmentEdgeManager.currentTime();
  41.  
  42. // 何时到期也为当前时间。意味着首次入队列时是没有延迟时间的。入列就可以出列
  43. this.whenToExpire = this.createTime;
  44. }
  45.  
  46. /**
  47. * @param maximumWait
  48. * @return True if we have been delayed > <code>maximumWait</code> milliseconds.
  49. */
  50. public boolean isMaximumWait(final long maximumWait) {
  51. return (EnvironmentEdgeManager.currentTime() - this.createTime) > maximumWait;
  52. }
  53.  
  54. /**
  55. * @return Count of times {@link #requeue(long)} was called; i.e this is
  56. * number of times we've been requeued.
  57. */
  58. public int getRequeueCount() {
  59. return this.requeueCount;
  60. }
  61.  
  62. /**
  63. * 相似又一次入列的处理方法,又一次入列次数requeueCount加1。何时到期未当前时间加參数when
  64. *
  65. * @param when When to expire, when to come up out of the queue.
  66. * Specify in milliseconds. This method adds EnvironmentEdgeManager.currentTime()
  67. * to whatever you pass.
  68. * @return This.
  69. */
  70. public FlushRegionEntry requeue(final long when) {
  71. this.whenToExpire = EnvironmentEdgeManager.currentTime() + when;
  72. this.requeueCount++;
  73. return this;
  74. }
  75.  
  76. /**
  77. * 推断何时到期的方法
  78. */
  79. @Override
  80. public long getDelay(TimeUnit unit) {
  81. // 何时到期减去当前时间
  82. return unit.convert(this.whenToExpire - EnvironmentEdgeManager.currentTime(),
  83. TimeUnit.MILLISECONDS);
  84. }
  85.  
  86. /**
  87. * 排序比較方法。依据推断何时到期的getDelay()方法来决定顺序
  88. */
  89. @Override
  90. public int compareTo(Delayed other) {
  91. // Delay is compared first. If there is a tie, compare region's hash code
  92. int ret = Long.valueOf(getDelay(TimeUnit.MILLISECONDS) -
  93. other.getDelay(TimeUnit.MILLISECONDS)).intValue();
  94. if (ret != 0) {
  95. return ret;
  96. }
  97.  
  98. // 何时到期时间一直的话,依据hashCode()来排序。事实上也就是依据HRegion的hashCode()方法返回值来排序
  99. FlushQueueEntry otherEntry = (FlushQueueEntry) other;
  100. return hashCode() - otherEntry.hashCode();
  101. }
  102.  
  103. @Override
  104. public String toString() {
  105. return "[flush region " + Bytes.toStringBinary(region.getRegionName()) + "]";
  106. }
  107.  
  108. @Override
  109. public int hashCode() {
  110. int hash = (int) getDelay(TimeUnit.MILLISECONDS);
  111. return hash ^ region.hashCode();
  112. }
  113.  
  114. @Override
  115. public boolean equals(Object obj) {
  116. if (this == obj) {
  117. return true;
  118. }
  119. if (obj == null || getClass() != obj.getClass()) {
  120. return false;
  121. }
  122. Delayed other = (Delayed) obj;
  123. return compareTo(other) == 0;
  124. }
  125. }
  126. }

FlushRegionEntry类有几个非常重要的对像:

1、HRegion region:待flush的HRegion;

2、long createTime:创建时间。

3、long whenToExpire:何时到期;

4、int requeueCount = 0:重入队列次数。

而它的对象在初始化时。创建时间createTime设置为当前时间。何时到期whenToExpire也为当前时间,它推断是否到期的getDelay()方法为何时到期减去当前时间,也就意味着首次入队列时是没有延迟时间的,入列就可以出列。

另外,它在队列内部用于排序的compareTo()方法。也是首先依据推断何时到期的getDelay()方法来决定顺序,何时到期时间一致的话,依据hashCode()来排序,事实上也就是依据HRegion的hashCode()方法返回值来排序。比較特别的是,这个类还提供了类似又一次入列的处理方法,又一次入列次数requeueCount加1。何时到期未当前时间加參数when,那么就相当于延期的了when时间变量。

说了那么多。接下来我们看下flush请求的实际处理流程,即FlushHandler的run()方法,其代码为:

  1. @Override
  2. public void run() {
  3. while (!server.isStopped()) {// HRegionServer未停止的话。run()方法一直执行
  4. FlushQueueEntry fqe = null;
  5.  
  6. try {
  7.  
  8. // 标志位AtomicBoolean类型的wakeupPending设置为false
  9. wakeupPending.set(false); // allow someone to wake us up again
  10.  
  11. // 从flushQueue队列中拉取一个FlushQueueEntry,即fqe
  12. fqe = flushQueue.poll(threadWakeFrequency, TimeUnit.MILLISECONDS);
  13.  
  14. if (fqe == null || fqe instanceof WakeupFlushThread) {// 假设fqe为空,或者为WakeupFlushThread
  15.  
  16. if (isAboveLowWaterMark()) {
  17. // 因为内存高于低阈值。flush线程唤醒
  18. LOG.debug("Flush thread woke up because memory above low water="
  19. + StringUtils.humanReadableInt(globalMemStoreLimitLowMark));
  20.  
  21. // 调用flushOneForGlobalPressure()方法,flush一个HRegion的MemStore,
  22. // 减少MemStore的大小。预防OOM等异常情况的发生
  23. if (!flushOneForGlobalPressure()) {
  24. // Wasn't able to flush any region, but we're above low water mark
  25. // This is unlikely to happen, but might happen when closing the
  26. // 这是不可能发生的。可是当关闭所有服务器时可能发生,另外一个线程正在flush region;
  27. // entire server - another thread is flushing regions. We'll just
  28. // sleep a little bit to avoid spinning, and then pretend that
  29. // we flushed one, so anyone blocked will check again
  30. // 我们将会休眠一段时间。以避免旋转,然后假装我们flush了一个region,以使得被堵塞线程再次检查
  31. Thread.sleep(1000);
  32. wakeUpIfBlocking();// 唤醒其它堵塞线程
  33. }
  34. // Enqueue another one of these tokens so we'll wake up again
  35. // 入列还有一个令牌。以使我们之后再次被唤醒
  36. wakeupFlushThread();
  37. }
  38. continue;
  39. }
  40.  
  41. // fre不为空,且不为WakeupFlushThread的话,转化为FlushRegionEntry类型的fre
  42. FlushRegionEntry fre = (FlushRegionEntry) fqe;
  43.  
  44. // 调用flushRegion()方法。而且假设结果为false的话。跳出循环
  45. if (!flushRegion(fre)) {
  46. break;
  47. }
  48. } catch (InterruptedException ex) {
  49. continue;
  50. } catch (ConcurrentModificationException ex) {
  51. continue;
  52. } catch (Exception ex) {
  53. LOG.error("Cache flusher failed for entry " + fqe, ex);
  54. if (!server.checkFileSystem()) {
  55. break;
  56. }
  57. }
  58. }
  59.  
  60. // 同一时候清空regionsInQueue和flushQueue
  61. // 又是在一起啊
  62. synchronized (regionsInQueue) {
  63. regionsInQueue.clear();
  64. flushQueue.clear();
  65. }
  66.  
  67. // Signal anyone waiting, so they see the close flag
  68. // 唤醒所有的等待着,使得它们可以看到close标志
  69. wakeUpIfBlocking();
  70.  
  71. // 记录日志信息
  72. LOG.info(getName() + " exiting");
  73. }

它的主要处理逻辑为:

1、首先HRegionServer未停止的话,run()方法一直执行。

2、将标志位AtomicBoolean类型的wakeupPending设置为false。

3、从flushQueue队列中拉取一个FlushQueueEntry。即fqe:

3.1、假设fqe为空,或者为WakeupFlushThread:

3.1.1、假设通过isAboveLowWaterMark()方法推断全局MemStore的大小高于限制值得低水平线。调用flushOneForGlobalPressure()方法,依照一定策略。flush一个HRegion的MemStore,减少MemStore的大小。预防OOM等异常情况的发生。并入列还有一个令牌,以使该线程之后再次被唤醒;

3.2、fre不为空,且不为WakeupFlushThread的话,转化为FlushRegionEntry类型的fre:调用flushRegion()方法,而且假设结果为false的话,跳出循环;

4、假设循环结束,同一时候清空regionsInQueue和flushQueue(ps:又是在一起啊O(∩_∩)O~)

5、唤醒全部的等待着,使得它们可以看到close标志。

6、记录日志。

我们注意到。WakeupFlushThread的主要作用是做为一个占位符或令牌插入到刷新队列flushQueue,以确保FlushHandler不会休眠,实际上WakeupFlushThread起到的作用不不过这个,在FlushHandler线程不断的poll刷新队列flushQueue中的元素时,假设获取到的是一个WakeupFlushThread,它会发起 一个检測,即RegionServer的全局MemStore大小是否超过低水平线,假设未超过,WakeupFlushThread只起到了一个占位符的作用,否则,WakeupFlushThread不仅做为占位符,保证刷新线程不休眠。还依照一定策略选择该RegionServer上的一个Region刷新memstore,以缓解RegionServer内存压力。

至于。假设全局MemStore的大小高于限制值得低水平线时。怎样选择一个HRegion进行flush以缓解MemStore压力,还有HRegion的flush是怎样发起的,我们下节再讲,敬请期待。

HBase源代码分析之HRegionServer上MemStore的flush处理流程(一)的更多相关文章

  1. HBase源代码分析之HRegionServer上MemStore的flush处理流程(二)

    继上篇文章<HBase源代码分析之HRegionServer上MemStore的flush处理流程(一)>遗留的问题之后,本文我们接着研究HRegionServer上MemStore的fl ...

  2. HBase源代码分析之HRegion上MemStore的flsuh流程(二)

    继上篇<HBase源代码分析之HRegion上MemStore的flsuh流程(一)>之后.我们继续分析下HRegion上MemStore flush的核心方法internalFlushc ...

  3. HBase源代码分析之HRegion上MemStore的flsuh流程(一)

    了解HBase架构的用户应该知道,HBase是一种基于LSM模型的分布式数据库.LSM的全称是Log-Structured Merge-Trees.即日志-结构化合并-树. 相比于Oracle普通索引 ...

  4. HBase源代码分析之MemStore的flush发起时机、推断条件等详情

    前面的几篇文章.我们具体介绍了HBase中HRegion上MemStore的flsuh流程,以及HRegionServer上MemStore的flush处理流程.那么,flush究竟是在什么情况下触发 ...

  5. HBase源代码分析之MemStore的flush发起时机、推断条件等详情(二)

    在<HBase源代码分析之MemStore的flush发起时机.推断条件等详情>一文中,我们具体介绍了MemStore flush的发起时机.推断条件等详情.主要是两类操作.一是会引起Me ...

  6. HBase源代码分析

    http://www.docin.com/p-647062205.html http://blog.csdn.net/luyee2010/article/category/1285622 http:/ ...

  7. HBase二次开发之搭建HBase调试环境,如何远程debug HBase源代码

    版本 HDP:3.0.1.0 HBase:2.0.0 一.前言 之前的文章也提到过,最近工作中需要对HBase进行二次开发(参照HBase的AES加密方法,为HBase增加SMS4数据加密类型).研究 ...

  8. Android4.42-Settings源代码分析之蓝牙模块Bluetooth(上)

    继上一篇Android系统源代码剖析(一)---Settings 接着来介绍一下设置中某个模块的源代码.本文依然是基于Android4.42源代码进行分析,分析一下蓝牙模块的实现.建议大致看一下关于S ...

  9. 1、Hbase原理分析

    一.Hbase介绍 1.1.对Hbase的认识 HBase作为面向列的数据库运行在HDFS之上,HDFS缺乏随机读写操作,HBase正是为此而出现. HBase参考 Google 的 Bigtable ...

随机推荐

  1. MYSQL学习笔记 (四)GROUP BY与HAVING用法

    注意:select 后的字段,必须要么包含在group by中,要么包含在having 后的聚合函数里. 1. GROUP BY 是分组查询, 一般 GROUP BY 是和聚合函数配合使用 group ...

  2. sybase数据库技术 :游标可更新与for read only/for update

    在定义游标时不指定for update 或 for read only,ASE会检查以了解游标是否可更新: 如果游标查询语句中包含order by子句,则ASE会将游标定义为只读:其它情况下定义为可更 ...

  3. POJ 1222 POJ 1830 POJ 1681 POJ 1753 POJ 3185 高斯消元求解一类开关问题

    http://poj.org/problem?id=1222 http://poj.org/problem?id=1830 http://poj.org/problem?id=1681 http:// ...

  4. 开源Word读写组件DocX介绍与入门

    来源:http://i.cnblogs.com/EditPosts.aspx?opt=1 读写Offic格式的文档,大家多少都有用到,可能方法也很多,组件有很多.这里不去讨论其他方法的优劣,只是向大家 ...

  5. 自己动手写js分享插件 [支持https] (可以分享QQ空间,微信,新浪微博。。。)

    由于百度分享,jiathis 等分享插件在https下均会报错,就萌生了自己动手写一个分享插件的念头,其实实现起来一点都不难,以下代码都已在https网站运行通过,特附上以下代码:还请各位看官不吝赐教 ...

  6. (转)h264中avc和flv数据的解析

    计算 AVCDecoderConfigurationRecord  得到 CodecPrivateData 数据(只有第一帧需要): 计算 NALUs 得到帧数据. 计算 AVCDecoderConf ...

  7. 基于Memcached的tomcat集群session共享所用的jar及多个tomcat各种序列化策略配置

    原文:http://www.cnblogs.com/interdrp/p/4096466.html 多个tomcat各种序列化策略配置如下:一.java默认序列化tomcat配置conf/contex ...

  8. 【jQuery】remove()和empty()的使用

    在项目需求中 ,会涉及,为select下拉框中重新填充或追加新的下拉项. 这样的话 之前的下拉项就需要清除掉. 有了这个需求,就需要我们注意到remove()和empty()的区别在哪里: empty ...

  9. 海思hi3518 opencv测试

    2.4.9的opencv 安装好交叉编译后,用Cmake 配置opencv 修改CMakeCache.txtCMAKE_EXE_LINKER_FLAGS:STRING=-lpthread -lrt - ...

  10. Spring3.0.5jar包用法详解 [转载]

    Spring3.X以后jar包进行了重构,取消了原来2.X版本中的总的spring.jar包,而是把总包中的功能全部分开打包.正在向osgi靠拢. 各个jar包详解如下: 1. org.springf ...