HBase源码分析之HRegionServer上的MovedRegionsCleaner工作线程

        MovedRegionsCleaner是什么呢?我们先来看下它在HRegionServer上的定义:

  /**
   * Chore to clean periodically the moved region list
   * 被移动Region列表的定期清理工作线程
   */
  private MovedRegionsCleaner movedRegionsCleaner;

        原来它是HRegionServer上一个被移动Region列表的定期清理工作线程。而它的类的定义如下:

  /**
   * Creates a Chore thread to clean the moved region cache.
   */
  protected static class MovedRegionsCleaner extends Chore implements Stoppable {
    private HRegionServer regionServer;
    Stoppable stoppable;

    // 私有构造方法
    private MovedRegionsCleaner(
      HRegionServer regionServer, Stoppable stoppable){
      super("MovedRegionsCleaner for region "+regionServer, TIMEOUT_REGION_MOVED, stoppable);
      this.regionServer = regionServer;
      this.stoppable = stoppable;
    }

    // 静态方法,通过其创建MovedRegionsCleaner实例
    static MovedRegionsCleaner createAndStart(HRegionServer rs){
      Stoppable stoppable = new Stoppable() {
        private volatile boolean isStopped = false;
        @Override public void stop(String why) { isStopped = true;}
        @Override public boolean isStopped() {return isStopped;}
      };

      return new MovedRegionsCleaner(rs, stoppable);
    }
  }

        我们发现它继承自Chore类,并且线程工作的频率为TIMEOUT_REGION_MOVED,也就是2分钟。这与之前我们讲过的HRegionServer上检查合并请求的compactionChecker、检查刷新请求的periodicFlusher是一样的,都继承了Chore类。关于Chore类,我们在之前的文章中已经讲过,这里就不再赘述了。

        我们看下它的构造方法,只有一个private的私有构造方法,同时它又提供了一个静态方法createAndStart(),这个方法被HRegionServer调用以构造MovedRegionsCleaner对象。而在HRegionServer中,MovedRegionsCleaner是如此被初始化的,代码如下:

    // Create the thread to clean the moved regions list
    // 创建movedRegionsCleaner工作线程去清理被移动Region列表
    movedRegionsCleaner = MovedRegionsCleaner.createAndStart(this);

        那么,MovedRegionsCleaner线程是如何工作的呢?按照之前讲的,集成自Chore的线程会周期性的调用chore()方法来执行需要做的工作。我们还是看下它的chore()方法,代码如下:

    // chore()方法就是调用HRegionServer的cleanMovedRegions()方法
    @Override
    protected void chore() {
      regionServer.cleanMovedRegions();
    }

        很简单,它实际调用的是regionServer实例的cleanMovedRegions()方法,代码如下:

  /**
   * Remove the expired entries from the moved regions list.
   */
  protected void cleanMovedRegions() {

	// 计算超时时间,当前时间减去固定值2分钟
	final long cutOff = System.currentTimeMillis() - TIMEOUT_REGION_MOVED;

	// 获取movedRegions集合的迭代器it
	Iterator<Entry<String, MovedRegionInfo>> it = movedRegions.entrySet().iterator();

	// 利用迭代器it遍历movedRegions集合中的元素
    while (it.hasNext()){

      // 取出movedRegions集合中的元素e,它是key-value类型,key为String类型的Region名称,value为MovedRegionInfo
      Map.Entry<String, MovedRegionInfo> e = it.next();

      // 根据MovedRegionInfo的MoveTime,即ts,与当前时间比较,
      // 如果当前时间已超过TIMEOUT_REGION_MOVED,则移除,
      // ts实际上是MovedRegionInfo的创建时间,也就是Region被移动的时间
      if (e.getValue().getMoveTime() < cutOff) {
        it.remove();
      }
    }
  }

        逻辑比较清晰,大体如下:

        1、首先计算超时时间,当前时间减去固定值2分钟,赋值给cutOff;

        2、获取movedRegions集合的迭代器it,实际上movedRegions就是HRegionServer上存储已被移动Regions的集合;

        3、利用迭代器it遍历movedRegions集合中的元素:

              3.1、取出movedRegions集合中的元素e,它是key-value类型,key为String类型的Region名称,value为MovedRegionInfo;

              3.2、根据MovedRegionInfo的MoveTime,即ts,与当前时间比较,如果当前时间已超过TIMEOUT_REGION_MOVED,则移除,ts实际上是MovedRegionInfo的创建时间,也就是Region被移动的时间。

        实际上,整个处理流程很简单,而MovedRegionInfo的代码如下:

  private static class MovedRegionInfo {
    private final ServerName serverName;
    private final long seqNum;
    private final long ts;

    public MovedRegionInfo(ServerName serverName, long closeSeqNum) {
      this.serverName = serverName;
      this.seqNum = closeSeqNum;
      ts = EnvironmentEdgeManager.currentTime();
     }

    public ServerName getServerName() {
      return serverName;
    }

    public long getSeqNum() {
      return seqNum;
    }

    public long getMoveTime() {
      return ts;
    }
  }

        其中,就包括一个重要的变量long类型的ts,它在构造方法中被赋值为当前时间,而被移动Region加入movedRegions时,是通过HRegionServer的addToMovedRegions()方法实现的,具体代码如下:

  protected void addToMovedRegions(String encodedName, ServerName destination, long closeSeqNum) {
    if (ServerName.isSameHostnameAndPort(destination, this.getServerName())) {
      LOG.warn("Not adding moved region record: " + encodedName + " to self.");
      return;
    }
    LOG.info("Adding moved region record: "
      + encodedName + " to " + destination + " as of " + closeSeqNum);
    movedRegions.put(encodedName, new MovedRegionInfo(destination, closeSeqNum));
  }

        我们发现,movedRegions集合中,key就是Region的encodedName,而value则是构造的一个对应包含目的地ServerName和关闭序列号closeSeqNum的MovedRegionInfo实例,而MovedRegionInfo在构造时,其ts的赋值上面已经展示了,就是当前时间啊。

        综上所述,MovedRegionsCleaner是HRegionServer上一个工作线程,它周期性的清理已被移动Region列表movedRegions中的到达时间的MovedRegionInfo信息,而线程工作的频率和MovedRegionInfo存活时间,均是TIMEOUT_REGION_MOVED,也就是2分钟。

        至于movedRegions集合中的数据是何时被添加的,为什么又要设计这种模式来移除其中的数据,我们后续再讲!

时间: 2024-10-01 13:54:10

HBase源码分析之HRegionServer上的MovedRegionsCleaner工作线程的相关文章

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

        继上篇文章<HBase源码分析之HRegionServer上MemStore的flush处理流程(一)>遗留的问题之后,本文我们接着研究HRegionServer上MemStore的flush处理流程,重点讲述下如何选择一个HRegion进行flush以缓解MemStore压力,还有HRegion的flush是如何发起的.         我们先来看下第一个问题:如何选择一个HRegion进行flush以缓解MemStore压力.上文中我们讲到过flush处理线程如果从flus

HBase源码分析之HRegionServer上MemStore的flush处理流程(一)

        在<HBase源码分析之HRegion上MemStore的flsuh流程(一)>.<HBase源码分析之HRegion上MemStore的flsuh流程(二)>等文中,我们介绍了HRegion上Memstore flush的主体流程和主要细节.但是,HRegion只是HBase表中按照行的方向对一片连续的数据区域的抽象,它并不能对外提供单独的服务,供客户端或者HBase其它实体调用.而HRegion上MemStore的flush还是要通过HRegionServer来

HBase源码分析之HRegionServer上compact流程分析

        前面三篇文章中,我们详细叙述了compact流程是如何在HRegion上进行的,了解了它的很多细节方面的问题.但是,这个compact在HRegionServer上是如何进行的?合并时文件是如何选择的呢?在这篇文章中,你将找到答案!         首先,在HRegionServer内部,我们发现,它定义了一个CompactSplitThread类型的成员变量compactSplitThread,单看字面意思,这就是一个合并分裂线程,那么它会不会就是HRegionServer上具

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

        继上篇<HBase源码分析之HRegion上MemStore的flsuh流程(一)>之后,我们继续分析下HRegion上MemStore flush的核心方法internalFlushcache(),它的主要流程如图所示:         其中,internalFlushcache()方法的代码如下: /** * Flush the memstore. Flushing the memstore is a little tricky. We have a lot of upda

HBase源码分析之HRegion上compact流程分析(三)

        在<HBase源码分析之HRegion上compact流程分析(二)>一文中,我们没有讲解真正执行合并的CompactionContext的compact()方法.现在我们来分析下它的具体实现.         首先,CompactionContext表示合并的上下文信息,它只是一个抽象类,其compact()并没有实现,代码如下: /** * Runs the compaction based on current selection. select/forceSelect

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

        了解HBase架构的用户应该知道,HBase是一种基于LSM模型的分布式数据库.LSM的全称是Log-Structured Merge-Trees,即日志-结构化合并-树.相比于Oracle普通索引所采用的B+树,LSM模型的最大特点就是,在读写之间采取一种平衡,牺牲部分读数据的性能,来大幅度的提升写数据的性能.通俗的讲,HBase写数据如此快,正是由于基于LSM模型,将数据写入内存和日志文件后即立即返回.         但是,数据始终在内存和日志中是不妥当的,首先内存毕竟是有

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

        前面的几篇文章,我们详细介绍了HBase中HRegion上MemStore的flsuh流程,以及HRegionServer上MemStore的flush处理流程.那么,flush到底是在什么情况下触发的呢?本文我们将详细探究下HBase中MemStore的flush流程的发起时机,看看到底都有哪些操作,或者哪些后台服务进程会触发MemStore的flush.         首先,在<HBase源码分析之HRegionServer上MemStore的flush处理流程(一)>和

HBase源码分析之compact请求发起时机、判断条件等详情(一)

        一般说来,任何一个比较复杂的分布式系统,针对能够使得其性能得到大幅提升的某一内部处理流程,必然有一个定期检查机制,使得该流程在满足一定条件的情况下,能够自发的进行,这样才能够很好的体现出复杂系统的自我适应与自我调节能力.我们知道,HBase内部的compact处理流程是为了解决MemStore Flush之后,文件数目太多,导致读数据性能大大下降的一种自我调节手段,它会将文件按照某种策略进行合并,大大提升HBase的数据读性能.那么,基于我刚才的陈述,compact流程是否有一个

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

        在<HBase源码分析之MemStore的flush发起时机.判断条件等详情>一文中,我们详细介绍了MemStore flush的发起时机.判断条件等详情,主要是两类操作,一是会引起MemStore数据大小变化的Put.Delete.Append.Increment等操作,二是会引起HRegion变化的诸如Regin的分裂.合并以及做快照时的复制拷贝等,同样会触发MemStore的flush流程.同时,在<HBase源码分析之compact请求发起时机.判断条件等详情(一