PendingReplicationBlocks实现了所有正在复制的数据块的记账工作。它实现以下三个主要功能:

1、记录此时正在复制的块;

2、一种对复制请求进行跟踪的粗粒度计时器;

3、一个定期识别未执行复制请求的线程。

我们先看下它内部有哪些成员变量,如下:

  1. // 块和正在进行的块复制信息的映射集合
  2. private final Map<Block, PendingBlockInfo> pendingReplications;
  3. // 复制请求超时的块列表
  4. private final ArrayList<Block> timedOutItems;
  5. // 后台工作线程
  6. Daemon timerThread = null;
  7. // 文件系统是否正在运行的标志位
  8. private volatile boolean fsRunning = true;
  9. //
  10. // It might take anywhere between 5 to 10 minutes before
  11. // a request is timed out.
  12. // 在一个请求超时之前可能需要5到10分钟
  13. // 请求超时阈值,默认为5分钟
  14. private long timeout = 5 * 60 * 1000;
  15. // 超时检查固定值:5分钟
  16. private final static long DEFAULT_RECHECK_INTERVAL = 5 * 60 * 1000;

首先是pendingReplications,它是块和正在进行的块复制信息的映射集合,所有正在复制的数据块及其对应复制信息都会被加入到这个集合。数据块复制信息PendingBlockInfo是对数据块开始复制时间timeStamp、待复制的目标数据节点列表List<DatanodeDescriptor>实例targets的一个封装,代码如下:

  1. /**
  2. * An object that contains information about a block that
  3. * is being replicated. It records the timestamp when the
  4. * system started replicating the most recent copy of this
  5. * block. It also records the list of Datanodes where the
  6. * replication requests are in progress.
  7. *
  8. * 正在被复制的块信息。它记录系统开始复制块最新副本的时间,也记录复制请求正在执行的数据节点列表。
  9. */
  10. static class PendingBlockInfo {
  11. // 时间戳
  12. private long timeStamp;
  13. // 待复制的目标数据节点列表
  14. private final List<DatanodeDescriptor> targets;
  15. // 构造方法
  16. PendingBlockInfo(DatanodeDescriptor[] targets) {
  17. // 时间戳赋值为当前时间
  18. this.timeStamp = now();
  19. this.targets = targets == null ? new ArrayList<DatanodeDescriptor>()
  20. : new ArrayList<DatanodeDescriptor>(Arrays.asList(targets));
  21. }
  22. long getTimeStamp() {
  23. return timeStamp;
  24. }
  25. // 设置时间戳为当前时间
  26. void setTimeStamp() {
  27. timeStamp = now();
  28. }
  29. // 增加复制数量,即增加目标数据节点
  30. void incrementReplicas(DatanodeDescriptor... newTargets) {
  31. if (newTargets != null) {
  32. for (DatanodeDescriptor dn : newTargets) {
  33. targets.add(dn);
  34. }
  35. }
  36. }
  37. // 减少复制数量,即减少目标数据节点
  38. void decrementReplicas(DatanodeDescriptor dn) {
  39. targets.remove(dn);
  40. }
  41. // 获取复制数量,即或许待复制的数据节点数目
  42. int getNumReplicas() {
  43. return targets.size();
  44. }
  45. }

它的构造方法中,即将时间戳timeStamp赋值为当前时间,并且提供了设置时间戳为当前时间的setTimeStamp()方法。同时提供了增加复制数量、减少复制数量、获取复制数量相关的三个方法,均是对待复制的目标数据节点列表的增加、减少与计数操作,上面注释很清楚,不再详述!

另外两个比较重要的变量就是复制请求超时的块列表timedOutItems和后台工作线程timerThread。由后台工作线程周期性的检查pendingReplications列表中的待复制数据块,看看其是否超时,如果超时的话,将其加入timedOutItems列表。后台工作线程timerThread的初始化如下:

  1. // 启动块复制监控线程
  2. void start() {
  3. timerThread = new Daemon(new PendingReplicationMonitor());
  4. timerThread.start();
  5. }

它实际上是借助PendingReplicationMonitor来完成的。PendingReplicationMonitor实现了Runnable接口,是一个周期性工作的线程,用于浏览从未完成它们复制请求的数据块,这个从未完成实际上就是在规定时间内还未完成的数据块复制信息。PendingReplicationMonitor的实现如下:

  1. /*
  2. * A periodic thread that scans for blocks that never finished
  3. * their replication request.
  4. * 一个周期性线程,用于浏览从未完成它们复制请求的数据块
  5. */
  6. class PendingReplicationMonitor implements Runnable {
  7. @Override
  8. public void run() {
  9. // 如果标志位fsRunning为true,即文件系统正常运行,则while循环一直进行
  10. while (fsRunning) {
  11. // 检查周期:取timeout,最高为5分钟
  12. long period = Math.min(DEFAULT_RECHECK_INTERVAL, timeout);
  13. try {
  14. // 检查方法
  15. pendingReplicationCheck();
  16. // 线程休眠period
  17. Thread.sleep(period);
  18. } catch (InterruptedException ie) {
  19. if(LOG.isDebugEnabled()) {
  20. LOG.debug("PendingReplicationMonitor thread is interrupted.", ie);
  21. }
  22. }
  23. }
  24. }
  25. /**
  26. * Iterate through all items and detect timed-out items
  27. * 通过所有项目迭代检测超时项目
  28. */
  29. void pendingReplicationCheck() {
  30. // 使用synchronized关键字对pendingReplications进行同步
  31. synchronized (pendingReplications) {
  32. // 获取集合pendingReplications的迭代器
  33. Iterator<Map.Entry<Block, PendingBlockInfo>> iter =
  34. pendingReplications.entrySet().iterator();
  35. // 记录当前时间now
  36. long now = now();
  37. if(LOG.isDebugEnabled()) {
  38. LOG.debug("PendingReplicationMonitor checking Q");
  39. }
  40. // 遍历pendingReplications集合中的每个元素
  41. while (iter.hasNext()) {
  42. // 取出每个<Block, PendingBlockInfo>条目
  43. Map.Entry<Block, PendingBlockInfo> entry = iter.next();
  44. // 取出Block对应的PendingBlockInfo实例pendingBlock
  45. PendingBlockInfo pendingBlock = entry.getValue();
  46. // 判断pendingBlock自其生成时的timeStamp以来到现在,是否已超过timeout时间
  47. if (now > pendingBlock.getTimeStamp() + timeout) {
  48. // 超过的话,
  49. // 取出timeout实例block
  50. Block block = entry.getKey();
  51. // 使用synchronized关键字对timedOutItems进行同步
  52. synchronized (timedOutItems) {
  53. // 将block添加入复制请求超时的块列表timedOutItems
  54. timedOutItems.add(block);
  55. }
  56. LOG.warn("PendingReplicationMonitor timed out " + block);
  57. // 从迭代器中移除该条目
  58. iter.remove();
  59. }
  60. }
  61. }
  62. }
  63. }

在它的run()方法内,如果标志位fsRunning为true,即文件系统正常运行,则while循环一直进行,然后在while循环内:

1、先取检查周期period:取timeout,最高为5分钟;

2、调用pendingReplicationCheck()方法进行检查;

3、线程休眠period时间,再次进入while循环。

pendingReplicationCheck的实现逻辑也很简单,如下:

使用synchronized关键字对pendingReplications进行同步:

1、获取集合pendingReplications的迭代器iter;

2、记录当前时间now;

3、遍历pendingReplications集合中的每个元素:

3.1、取出每个<Block, PendingBlockInfo>条目;

3.2、取出Block对应的PendingBlockInfo实例pendingBlock;

3.3、判断pendingBlock自其生成时的timeStamp以来到现在,是否已超过timeout时间,超过的话:

3.3.1、取出timeout实例block;

3.3.2、使用synchronized关键字对timedOutItems进行同步,使用synchronized关键字对timedOutItems进行同步;

3.3.3、从迭代器中移除该条目。

PendingReplicationBlocks还提供了获取复制超时块数组的getTimedOutBlocks()方法,代码如下:

  1. /**
  2. * Returns a list of blocks that have timed out their
  3. * replication requests. Returns null if no blocks have
  4. * timed out.
  5. * 返回一个其复制请求已超时的数据块列表,如果没有则返回null
  6. */
  7. Block[] getTimedOutBlocks() {
  8. / 使用synchronized关键字对timedOutItems进行同步
  9. synchronized (timedOutItems) {
  10. // 如果timedOutItems中没有数据,则直接返回null
  11. if (timedOutItems.size() <= 0) {
  12. return null;
  13. }
  14. // 将Block列表timedOutItems转换成Block数组
  15. Block[] blockList = timedOutItems.toArray(
  16. new Block[timedOutItems.size()]);
  17. // 清空Block列表timedOutItems
  18. timedOutItems.clear();
  19. // 返回Block数组
  20. return blockList;
  21. }
  22. }

PendingReplicationBlocks另外还提供了增加一个块到正在进行的块复制信息列表中的increment()方法和减少正在复制请求的数量的decrement()方法,代码如下:

  1. /**
  2. * Add a block to the list of pending Replications
  3. * 增加一个块到正在进行的块复制信息列表中
  4. *
  5. * @param block The corresponding block
  6. * @param targets The DataNodes where replicas of the block should be placed
  7. */
  8. void increment(Block block, DatanodeDescriptor[] targets) {
  9. // 使用synchronized关键字对pendingReplications进行同步
  10. synchronized (pendingReplications) {
  11. // 根据Block实例block先从集合pendingReplications中查找
  12. PendingBlockInfo found = pendingReplications.get(block);
  13. if (found == null) {
  14. // 如果没有找到,直接put进去,利用DatanodeDescriptor[]的实例targets构造PendingBlockInfo对象
  15. pendingReplications.put(block, new PendingBlockInfo(targets));
  16. } else {
  17. // 如果之前存在,增加复制数量,即增加目标数据节点
  18. found.incrementReplicas(targets);
  19. // 设置时间戳为当前时间
  20. found.setTimeStamp();
  21. }
  22. }
  23. }
  24. /**
  25. * One replication request for this block has finished.
  26. * Decrement the number of pending replication requests
  27. * for this block.
  28. * 针对给定数据块的一个复制请求已完成。针对该数据块,减少正在复制请求的数量。
  29. *
  30. * @param The DataNode that finishes the replication
  31. */
  32. void decrement(Block block, DatanodeDescriptor dn) {
  33. // 使用synchronized关键字对pendingReplications进行同步
  34. synchronized (pendingReplications) {
  35. // 根据Block实例block先从集合pendingReplications中查找
  36. PendingBlockInfo found = pendingReplications.get(block);
  37. if (found != null) {
  38. if(LOG.isDebugEnabled()) {
  39. LOG.debug("Removing pending replication for " + block);
  40. }
  41. // 减少复制数量,即减少目标数据节点
  42. found.decrementReplicas(dn);
  43. // 如果数据块对应的复制数量总数小于等于0,复制工作完成,
  44. // 直接从pendingReplications集合中移除该数据块及其对应信息
  45. if (found.getNumReplicas() <= 0) {
  46. pendingReplications.remove(block);
  47. }
  48. }
  49. }
  50. }

以及统计块数量和块复制数量的方法,如下:

  1. /**
  2. * The total number of blocks that are undergoing replication
  3. * 正在被复制的块的总数
  4. */
  5. int size() {
  6. return pendingReplications.size();
  7. }
  8. /**
  9. * How many copies of this block is pending replication?
  10. * 块复制的总量
  11. */
  12. int getNumReplicas(Block block) {
  13. synchronized (pendingReplications) {
  14. PendingBlockInfo found = pendingReplications.get(block);
  15. if (found != null) {
  16. return found.getNumReplicas();
  17. }
  18. }
  19. return 0;
  20. }

上述方法代码逻辑都很简单,而且注释也很详细,此处不再过多赘述!

HDFS源码分析数据块复制之PendingReplicationBlocks的更多相关文章

  1. HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

    HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

  2. HDFS源码分析数据块复制监控线程ReplicationMonitor(一)

    ReplicationMonitor是HDFS中关于数据块复制的监控线程,它的主要作用就是计算DataNode工作,并将复制请求超时的块重新加入到待调度队列.其定义及作为线程核心的run()方法如下: ...

  3. HDFS源码分析数据块复制选取复制源节点

    数据块的复制当然需要一个源数据节点,从其上拷贝数据块至目标数据节点.那么数据块复制是如何选取复制源节点的呢?本文我们将针对这一问题进行研究. 在BlockManager中,chooseSourceDa ...

  4. HDFS源码分析数据块校验之DataBlockScanner

    DataBlockScanner是运行在数据节点DataNode上的一个后台线程.它为所有的块池管理块扫描.针对每个块池,一个BlockPoolSliceScanner对象将会被创建,其运行在一个单独 ...

  5. HDFS源码分析数据块汇报之损坏数据块检测checkReplicaCorrupt()

    无论是第一次,还是之后的每次数据块汇报,名字名字节点都会对汇报上来的数据块进行检测,看看其是否为损坏的数据块.那么,损坏数据块是如何被检测的呢?本文,我们将研究下损坏数据块检测的checkReplic ...

  6. HDFS源码分析数据块之CorruptReplicasMap

    CorruptReplicasMap用于存储文件系统中所有损坏数据块的信息.仅当它的所有副本损坏时一个数据块才被认定为损坏.当汇报数据块的副本时,我们隐藏所有损坏副本.一旦一个数据块被发现完好副本达到 ...

  7. HDFS源码分析之数据块及副本状态BlockUCState、ReplicaState

    关于数据块.副本的介绍,请参考文章<HDFS源码分析之数据块Block.副本Replica>. 一.数据块状态BlockUCState 数据块状态用枚举类BlockUCState来表示,代 ...

  8. HDFS源码分析心跳汇报之数据块汇报

    在<HDFS源码分析心跳汇报之数据块增量汇报>一文中,我们详细介绍了数据块增量汇报的内容,了解到它是时间间隔更长的正常数据块汇报周期内一个smaller的数据块汇报,它负责将DataNod ...

  9. HDFS源码分析心跳汇报之数据块增量汇报

    在<HDFS源码分析心跳汇报之BPServiceActor工作线程运行流程>一文中,我们详细了解了数据节点DataNode周期性发送心跳给名字节点NameNode的BPServiceAct ...

随机推荐

  1. WPF 自动选择dll,以SQLite为例

    在学习sqlite的过程中,发现它的dll是区分32位和64位的,起初觉得很恼火,但是仔细看了下, 发现让程序自行选择dll其实也不是一件很麻烦的事情,如下: 1>创建一个sqlite数据 2& ...

  2. css sticky footer 布局 手机端

    什么是css sticky footer 布局? 通常在手机端写页面 会遇到如下情况 页面长度很短不足以撑起一屏,此时希望页脚在页面的底部 而当页面超过一屏时候,页脚会在文章的底部 ,网上有许多办法, ...

  3. MVC中的过滤器/拦截器怎么写

    创建一个AuthenticateFilterAttribute(即过滤器/拦截器) 引用System.Web.Mvc; public class AuthenticateFilterAttribute ...

  4. 常见的 Git 命令:

    开始一个工作区(参见:git help tutorial) clone 克隆一个仓库到一个新目录 init 创建一个空的 Git 仓库或重新初始化一个已存在的仓库 在当前变更上工作(参见:git he ...

  5. Qualcomm download 所需要的 contents.xml

    Platform MSM8917 PM8937 PMI8940 在 Qualcomm code base 中, amss下有許多 MSM89xx 之類的 folder, 這些是為了不同 chip 所產 ...

  6. mdev详解【转】

    转自:http://blog.chinaunix.net/uid-29401328-id-5019678.html 一.概述 mdev是busybox提供的一个工具,用在嵌入式系统中,相当于简化版的u ...

  7. C++学习(一):现代C++尝试

    C++是一门与时俱进的语言. 早期的C++关注的主要问题是通用性,却没有太多关注易用性的问题,使得C++成为了一门多范式语言,但是使用门槛较高. 从2011开始,C++的标准进行了较大的更新,开始更多 ...

  8. 【C/C++】快速排序的两种实现思路

    方法一:不断填坑,一次确定一个值.http://blog.csdn.net/morewindows/article/details/6684558 #include<stdio.h> vo ...

  9. 转:Java并发编程:volatile关键字解析

    Java并发编程:volatile关键字解析 Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字, ...

  10. 利用js实现table增加一行

    简单的方法: 用jquery插件,比如设置该table的id为mytable <table id="mytable"> <tr> <td> 第一 ...