数据存储结点主类。
首先启动一系列服务端口,如接收数据的端口,web server 访问端口等。
然后调用startDataNode() 函数去做以下事情。
启动 DN 的数据接收服务守护线程 DataXceiverServer 。
循环判断是否需要更新,如参数发生变化了,则需要重新初始化 DN 。
然后再发送心跳,发送最近接收的 block ,报告 DN 当前的 block 列表给 NN 。
报告 DN 当前的所有 block 列表的时间间隔相对要长很多,默认是 1 个小时报告一次。
run()
dataXceiverServer .start();
while ( shouldRun ) {
startDistributedUpgradeIfNeeded();
offerService();
}
offerService()
也是一个循环,首先会计算是否到了心跳时间。
到了下一次心跳时间,则远程调用NN 的发送心跳函数sendHeartbeat, 会返回相应的值 。然后处理返回的值。
当下一次心跳还没到时,则向NN 报告最近收到的block 。
然后,再向NN 报告当前所有的blocks 。处理NN 返回的值。
如果数据块扫描线程没有启动,则启动它。
每次与NN 通信时,都会返回DatanodeCommand 命令,然后DN 调用processCommand() 函数去处理这些命令。
while ( shouldRun ) {
try {
long startTime = now ();
if (startTime - lastHeartbeat > heartBeatInterval ) {
lastHeartbeat = startTime ;
DatanodeCommand[] cmds = namenode .sendHeartbeat( dnRegistration ,
data .getCapacity(),
data .getDfsUsed(),
data .getRemaining(),
xmitsInProgress .get(),
getXceiverCount());
myMetrics.heartbeats.inc(now () - startTime);
//LOG.info("Just sent heartbeat, with name " + localName);
if (!processCommand(cmds))
continue ;
}
reportReceivedBlocks();
DatanodeCommand cmd = blockReport() ;
processCommand(cmd);
// start block scanner
if ( blockScanner != null && blockScannerThread == null &&
upgradeManager .isUpgradeCompleted()) {
LOG .info( "Starting Periodic block scanner." );
blockScannerThread = new Daemon( blockScanner );
blockScannerThread .start();
}
long waitTime = heartBeatInterval - (System.currentTimeMillis () - lastHeartbeat );
synchronized ( receivedBlockList ) {
if (waitTime > 0 && receivedBlockList .size() == 0) {
try {
receivedBlockList .wait(waitTime);
} catch (InterruptedException ie) {
}
}
} // synchronized
}
} // while (shouldRun)
} // offerService
sendHeartbeat()
心跳的时间间隔为参数,默认为3S 。
this . heartBeatInterval = conf.getLong( "dfs.heartbeat.interval" , HEARTBEAT_INTERVAL ) * 1000L;
processCommand()
从NN 中收到的命令有以下:
final static int DNA_UNKNOWN = 0; // unknown action
final static int DNA_TRANSFER = 1; // transfer blocks to another datanode
final static int DNA_INVALIDATE = 2; // invalidate blocks
final static int DNA_SHUTDOWN = 3; // shutdown node
final static int DNA_REGISTER = 4; // re-register
final static int DNA_FINALIZE = 5; // finalize previous upgrade
final static int DNA_RECOVERBLOCK = 6; // request a block recovery
final static int DNA_ACCESSKEYUPDATE = 7; // update access key
将block 传送给其他的DN, 则调用BlockSender 类将block 传送给其他DN 。
标识无效的block ,调用FSDataset 类中的方法去标识数据块状态。
恢复block 。
关闭DN 。
重新注册DN 。
完成升级。
private boolean processCommand(DatanodeCommand cmd) throws IOException {
if (cmd == null )
return true ;
final BlockCommand bcmd = cmd instanceof BlockCommand? (BlockCommand)cmd: null ;
switch (cmd.getAction()) {
case DatanodeProtocol. DNA_TRANSFER :
// Send a copy of a block to another datanode
transferBlocks(bcmd.getBlocks(), bcmd.getTargets());
myMetrics.blocksReplicated.inc(bcmd.getBlocks().length);
break ;
case DatanodeProtocol. DNA_INVALIDATE :
//
// Some local block(s) are obsolete and can be
// safely garbage-collected.
//
Block toDelete[] = bcmd.getBlocks();
try {
if ( blockScanner != null ) {
blockScanner .deleteBlocks(toDelete);
}
data .invalidate(toDelete);
} catch (IOException e) {
checkDiskError();
throw e;
}
myMetrics.blocksRemoved.inc(toDelete. length );
break ;
case DatanodeProtocol. DNA_SHUTDOWN :
// shut down the data node
this .shutdown();
return false ;
case DatanodeProtocol. DNA_REGISTER :
// namenode requested a registration - at start or if NN lost contact
LOG .info( "DatanodeCommand action: DNA_REGISTER" );
if ( shouldRun ) {
register();
}
break ;
case DatanodeProtocol. DNA_FINALIZE :
storage .finalizeUpgrade();
break ;
case UpgradeCommand. UC_ACTION_START_UPGRADE :
// start distributed upgrade here
processDistributedUpgradeCommand((UpgradeCommand)cmd);
break ;
case DatanodeProtocol. DNA_RECOVERBLOCK :
recoverBlocks(((BlockRecoveryCommand)cmd).getRecoveringBlocks());
break ;
case DatanodeProtocol. DNA_ACCESSKEYUPDATE :
LOG .info( "DatanodeCommand action: DNA_ACCESSKEYUPDATE" );
if ( isAccessTokenEnabled ) {
accessTokenHandler .setKeys(((KeyUpdateCommand) cmd).getExportedKeys());
}
break ;
default :
LOG .warn( "Unknown DatanodeCommand action: " + cmd.getAction());
}
return true ;
}
reportReceivedBlocks()
在DN 类中有二个变量。
private LinkedList<Block> receivedBlockList = new LinkedList<Block>();
private LinkedList<String> delHints = new LinkedList<String>();
最近收到的blocks 。
当完整的接收到一个block 时,会将block 信息添加到这个队列里。
blockReport()
报告数据块列表的时间间隔参数,默认为60 * 60 * 1000;
this . blockReportInterval =
conf.getLong( "dfs.blockreport.intervalMsec" , BLOCKREPORT_INTERVAL );
调用FSDataSet 类中的getBlockReport() 方法来获得正确状态的数据块列表。在该类中专门有个变量来保存数据块列表。
ReplicasMap volumeMap = new ReplicasMap();
同样,当block 完成到某个状态后,都会block 添加到这个队列中。
同时返回给NN 的,finalized 状态的block 列表。
未完,待续。