您的位置:首页 > 大数据 > Hadoop

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

2016-06-03 14:48 477 查看
无论是第一次,还是之后的每次数据块汇报,名字名字节点都会对汇报上来的数据块进行检测,看看其是否为损坏的数据块。那么,损坏数据块是如何被检测的呢?本文,我们将研究下损坏数据块检测的checkReplicaCorrupt()方法。

关于数据块及其副本的状态,请阅读《HDFS源码分析之数据块及副本状态BlockUCState、ReplicaState》一文。

checkReplicaCorrupt()方法专门用于损坏数据块检测,代码如下:

[java] view plain copy







/**

* The next two methods test the various cases under which we must conclude

* the replica is corrupt, or under construction. These are laid out

* as switch statements, on the theory that it is easier to understand

* the combinatorics of reportedState and ucState that way. It should be

* at least as efficient as boolean expressions.

*

* @return a BlockToMarkCorrupt object, or null if the replica is not corrupt

*/

private BlockToMarkCorrupt checkReplicaCorrupt(

Block reported, ReplicaState reportedState,

BlockInfo storedBlock, BlockUCState ucState,

DatanodeDescriptor dn) {

// 检测数据节点DataNode上的数据块副本状态ReplicaState实例reportedState

switch(reportedState) {

case FINALIZED:// 数据块副本如果为FINALIZED状态,即没有被修改的状态

// 需要再看名字节点数据块状态BlockUCState,即ucState

switch(ucState) {

case COMPLETE:// 如果是COMPLETE状态,不应被汇报为坏块:这种情况下,数据节点副本已经处于不会被修改的FINALIZED状态,

// 而名字节点中的数据块状态为COMPLETE,也是不会被修改的状态,

// 并且其他数据节点已经汇报过该数据块对应的一个副本,所以不会是损坏的数据块

case COMMITTED:// 如果是COMMITTED状态,虽然数据块不会被修改,但是还没有任何数据节点汇报过副本,还需要做以下时间戳和大小的判断:

if (storedBlock.getGenerationStamp() != reported.getGenerationStamp()) {

// 如果存储的数据块时间戳不等于汇报的数据块时间戳,需要汇报为坏块,

// 且损坏原因为Reason.GENSTAMP_MISMATCH,即时间戳不匹配

final long reportedGS = reported.getGenerationStamp();

return new BlockToMarkCorrupt(storedBlock, reportedGS,

"block is " + ucState + " and reported genstamp " + reportedGS

+ " does not match genstamp in block map "

+ storedBlock.getGenerationStamp(), Reason.GENSTAMP_MISMATCH);

} else if (storedBlock.getNumBytes() != reported.getNumBytes()) {

// 如果存储的数据块大小不等于汇报的数据块大小,需要汇报为坏块

// 且损坏原因为Reason.SIZE_MISMATCH,即大小不匹配

return new BlockToMarkCorrupt(storedBlock,

"block is " + ucState + " and reported length " +

reported.getNumBytes() + " does not match " +

"length in block map " + storedBlock.getNumBytes(),

Reason.SIZE_MISMATCH);

} else {

// 其它情况下不是一个坏块

return null; // not corrupt

}

case UNDER_CONSTRUCTION:// 如果是UNDER_CONSTRUCTION状态,数据还在被写入,所以需要做以下时间戳的判断:

if (storedBlock.getGenerationStamp() > reported.getGenerationStamp()) {

// 如果存储的数据块时间戳不等于汇报的数据块时间戳,需要汇报为坏块,

// 且损坏原因为Reason.GENSTAMP_MISMATCH,即时间戳不匹配

final long reportedGS = reported.getGenerationStamp();

return new BlockToMarkCorrupt(storedBlock, reportedGS, "block is "

+ ucState + " and reported state " + reportedState

+ ", But reported genstamp " + reportedGS

+ " does not match genstamp in block map "

+ storedBlock.getGenerationStamp(), Reason.GENSTAMP_MISMATCH);

}

return null;

default:

// 其他情况下均不是损坏的数据块

return null;

}

case RBW:// 数据块副本为正在被写入状态,不应被汇报为坏块

case RWR:// 数据块副本为正等待被恢复状态

if (!storedBlock.isComplete()) {

// 如果存储的数据块不是Complete状态,则不是一个坏块

return null; // not corrupt

} else if (storedBlock.getGenerationStamp() != reported.getGenerationStamp()) {

// 否则需要判断时间戳是否一致

final long reportedGS = reported.getGenerationStamp();

return new BlockToMarkCorrupt(storedBlock, reportedGS,

"reported " + reportedState + " replica with genstamp " + reportedGS

+ " does not match COMPLETE block's genstamp in block map "

+ storedBlock.getGenerationStamp(), Reason.GENSTAMP_MISMATCH);

} else { // COMPLETE block, same genstamp

if (reportedState == ReplicaState.RBW) {

// 如果数据块副本为正在写入状态RBW,不会是一个损坏的数据块

// If it's a RBW report for a COMPLETE block, it may just be that

// the block report got a little bit delayed after the pipeline

// closed. So, ignore this report, assuming we will get a

// FINALIZED replica later. See HDFS-2791

LOG.info("Received an RBW replica for " + storedBlock +

" on " + dn + ": ignoring it, since it is " +

"complete with the same genstamp");

return null;

} else {

// 否则为一个损坏的数据块,且损坏原因为Reason.INVALID_STATE

return new BlockToMarkCorrupt(storedBlock,

"reported replica has invalid state " + reportedState,

Reason.INVALID_STATE);

}

}

case RUR: // should not be reported 副本处于恢复状态下,不应被汇报为坏块

case TEMPORARY: // should not be reported 副本为仅为复制而创建的临时副本,不应被汇报为坏块

default:

String msg = "Unexpected replica state " + reportedState

+ " for block: " + storedBlock +

" on " + dn + " size " + storedBlock.getNumBytes();

// log here at WARN level since this is really a broken HDFS invariant

LOG.warn(msg);

return new BlockToMarkCorrupt(storedBlock, msg, Reason.INVALID_STATE);

}

}

checkReplicaCorrupt()方法处理逻辑略显复杂,但是还算清晰,它需要被汇报的数据块Block实例reported、副本状态实例ReplicaState、数据块状态BlockUCState等参数,主要处理逻辑如下:

基于数据节点DataNode上的数据块副本状态ReplicaState实例reportedState进行检测,如果:

1、数据块副本状态ReplicaState为正在写入RBW、正在恢复RUR、为复制而创建的临时副本TEMPORARY三种状态,则肯定不是损坏的数据块,因为这些数据块副本还处于不确定的状态,还需要被写入或者被舍弃等;

2、数据块副本状态ReplicaState为FINALIZED的话,说明数据已被完全写入,数据块副本大小及时间戳均不会再发生变化,此时,就等着其所在数据节点DataNode进行数据块汇报,将该数据块副本汇报给名字节点NameNode,那么我们需要看数据块Block在名字节点内的状态:

2.1、如果是COMPLETE状态,不应被汇报为坏块:这种情况下,数据节点副本已经处于不会被修改的FINALIZED状态,而名字节点中的数据块状态为COMPLETE,也是不会被修改的状态,并且其他数据节点已经汇报过该数据块对应的一个副本,所以不会是损坏的数据块;

2.2、如果是COMMITTED状态,虽然数据块不会被修改,但是还没有任何数据节点汇报过副本,还需要做以下时间戳和大小的判断:

2.2.1、如果名字节点存储的数据块时间戳不等于汇报的数据块时间戳,需要汇报为坏块,且损坏原因为Reason.GENSTAMP_MISMATCH,即时间戳不匹配;

2.2.2、如果名字节点存储的数据块大小不等于汇报的数据块大小,需要汇报为坏块,且损坏原因为Reason.SIZE_MISMATCH,即大小不匹配;

2.2.3、其它情况下不是一个坏块;

2.3、如果是UNDER_CONSTRUCTION状态,数据还在被写入,所以可以忽略大小,只做以下时间戳的判断:

2.3.1、如果名字节点存储的数据块时间戳不等于汇报的数据块时间戳,需要汇报为坏块,且损坏原因为Reason.GENSTAMP_MISMATCH,即时间戳不匹配;

2.3.2、其它情况下不是一个坏块;

2.4、其他情况下均不是损坏的数据块;

3、数据块副本为正等待被恢复状态RWR的话,需要看数据块在名字节点NameNode的状态:

3.1、如果名字节点存储的数据块Block不是COMPLETE状态,则不是一个坏块,此时数据块尚未上报过名字节点NameNode;

3.2、如果名字节点存储的数据块是COMPLETE状态,说明之前已经上报过,需要判断时间戳是否一致,如果时间戳不一致的话,则说明其是一个坏块;

3.3、如果名字节点存储的数据块是COMPLETE状态,说明之前已经上报过,且时间戳一致的话,如果数据块副本为正在写入状态RBW,不会是一个损坏的数据块,否则为一个损坏的数据块,且损坏原因为Reason.INVALID_STATE;

4、其他情况下均为一个坏块,且损坏原因为Reason.INVALID_STATE。

BlockToMarkCorrupt是一个数据块标记为坏块的抽象数据结构,它包含四个成员变量,如下:

[java] view plain copy







/** The corrupted block in a datanode. */

数据节点上的坏块数据块信息

final BlockInfo corrupted;

/** The corresponding block stored in the BlockManager. */

// 名字节点BlockManager中存储的相应的数据块信息

final BlockInfo stored;

// 损坏原因:字符串类型

/** The reason to mark corrupt. */

final String reason;

// 损坏原因code:枚举类型

/** The reason code to be stored */

final Reason reasonCode;

其他的均是构造方法,及覆写的toString()方法,不再赘述!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: