DataNode的分析
来源:互联网 发布:网络教育几月交学费 编辑:程序博客网 时间:2024/05/19 17:07
相对NN,DN主要就是对数据块的副本进行操作,如增删改等操作,管理DN中的这些副本,另外提供对副本的接口给client,NN,其他的DN。
startDataNode()方法:
首先从配置文件中读取与DN相关的配置参数。
与NN进行握手。
根据参数配置好的数据块存放的文件目录,为每个目录建立起DataStorage,然后调用该类的recoverTransitionRead方法去读取存储元信息,锁住目录,然后转变文件状态。做一些格式化,恢复,回滚,升级操作。
然后调用FSDataset构造函数,读取上一步的文件目录下的副本文件,将副本状态信息保存到内存列队中,同时创建线程池用于执行对副本的异步删除操作。
实始化数据块扫描类DataBlockScanner。
最后,启动一系列服务端口,如接收数据的端口,web server访问端口等。
run()方法:
首先启动DN的数据接收服务守护线程DataXceiverServer。
然后循环做以下操作:
判断是否需要更新,如参数发生变化了,则需要重新初始化DN。
发送心跳,发送最近接收的block,报告DN当前的block列表给NN。
接收NN发送给DN的相关命令,并且执行命令。
报告DN当前的所有block列表的时间间隔相对要长很多,默认是1个小时报告一次。
启动DataBlockScanner线程。
NN向DN发送的命令
由DataNode类中的processCommand()方法完成。
副本复制:
由NN告诉DN将DN中存在的block传送给其他DN,主要是由于NN检测到该数据块的副本数少于3,因此需要将副本复制给其他DN,于是通知副本所在的DN,将副本数据推送给其他的DN。其中NN将数据块的信息和目标的DN信息存放在类BlockCommand中,DN收到这些信息后,解析出数据块和目标DN信息,分别将相应的数据块传送给相应的目标DN。
private void transferBlocks(Block blocks[],
DatanodeInfoxferTargets[][]
) {
for (int i = 0; i < blocks.length; i++) {
try {
transferBlock(blocks[i],xferTargets[i]);
} catch (IOException ie) {
LOG.warn("Failed to transfer block " + blocks[i], ie);
}
}
}
然后判断该副本的在DN磁盘上的大小小于NN中存放的大小时,则该副本是不可用的,然后调用namenode.reportBadBlocks()告诉NN,这副本是坏的。
否则启动线程去进行副本传送工作。
new Daemon(new DataTransfer(xferTargets,block, this)).start();
DataTransfer线程里面将会调用BlockSender. sendBlock()将副本数据分成多个packs传送给相应的目标DN。
标识无效的数据块:
同样从NN中拿到无效的数据块,因为客户已经将数据块删除了。
一方面是告诉DataBlockScanner不再去对该数据块进行扫描检查工作。
另一方面,告诉FSDataset,从内存的副本集合中删除掉该副本,同时调用FSDatasetAsyncDiskService类deleteAsync()异步的删除该副本在本地磁盘上的数据文件和元信息文件。
if (blockScanner != null) {
blockScanner.deleteBlocks(toDelete);
}
data.invalidate(toDelete);
数据块恢复:
将为每个要恢复的数据块建立一个线程来完成恢复工作。
public Daemon recoverBlocks(final Collection<RecoveringBlock> blocks){
Daemon d = new Daemon(threadGroup, new Runnable() {
/** Recover a list of blocks. It is run by the primary datanode. */
public void run() {
for(RecoveringBlock b :blocks) {
try {
logRecoverBlock("NameNode", b.getBlock(), b.getLocations());
recoverBlock(b);
} catch (IOException e) {
LOG.warn("recoverBlocks FAILED: " + b, e);
}
}
}
});
d.start();
return d;
}
然后分别去该数据块的其他DN上查询到在这些DN上副本记录。
然后进行数据块恢复工作。
如果所有的DN上该数据块都没有保存数据,则告诉调用
namenode.commitBlockSynchronization(block,recoveryId, 0,
true, true, DatanodeID.EMPTY_ARRAY);
告诉NN,这是一个空的数据块。
不然的话,计算该数据块最佳可用的副本状态。即取所有的DN中收到数据值最小作为可用的数据。
最后,在将该数据块的状态告诉其他的DN和NN。
Block reply = r.datanode.updateReplicaUnderRecovery(
r.rInfo, recoveryId, newBlock.getNumBytes());
namenode.commitBlockSynchronization(block,
newBlock.getGenerationStamp(),newBlock.getNumBytes(), true, false,
nlist);
关闭DN:
将DN中的守护线程都停止掉。如:DataXceiverServer,DataBlockScanner,DataNode的心跳线程,以及与NN的RPC通信线程等。
重新注册:
调用RPC,告之NN,DN当前的通信信息。
dnRegistration = namenode.registerDatanode(dnRegistration);
另外,设置BR的周期。
scheduleBlockReport(initialBlockReportDelay);
完成升级:
由于升级完成,因为在升级之前,为了保证数据不会丢失,将当前的文件目录都从.cuttent移到了.Previous,升级成功后,将.pervious目录移到.tmp目录下,然后删除.tmp临时目录下的所有数据。
client,其他的DN与该DN的数据传输
由DataXceiver类中的run()方法完成。
读数据块:
建立起向客户端的输出流,然后调用BlockSender类将数据块副本的数据从磁盘读取后发送给客户端。
对应于客户端来DN中读取相应的数据块。
写数据块:
收到一个数据块的数据,将其写到磁盘上,同时要向下一个DN传送该数据块的数据。
对应于客户端或者上游DN向本地DN写数据。
替换数据块
向相应的DN发送复制操作,然后接收该DN发送来的数据块数据,并且将数据保存到磁盘中。
复制数据块
从磁盘中读取到数据块的数据,然后将数据发送给发送复制操作给自已的目标DN。
获得数据块的校验数据:
从元数据文件里获得校验数据,然后发送给客户端。
DataStorage
存储信息的管理类。
protected List<StorageDirectory> storageDirs = newArrayList<StorageDirectory>();
变量保存着文件目录列表。
recoverTransitionRead()
DN构造时会调用该函数,在读文件目录前做回滚和保存现场,用于防止启动失败,数据丢失。
void recoverTransitionRead(NamespaceInfonsInfo,
Collection<File> dataDirs,
StartupOptionstartOpt
)
为每个数据目录计算状态,确定一致性。根据当前的不同状态进行格式化或者恢复操作。
参数dfs.datanode.data.dir中由管理员配置副本存放的文件目录,可以有多个目录,用,分开。
switch(curState) {
case NORMAL:
break;
case NON_EXISTENT:
// ignore thisstorage
LOG.info("Storagedirectory " + dataDir + " does not exist.");
it.remove();
continue;
case NOT_FORMATTED: // format
LOG.info("Storagedirectory " + dataDir + " is not formatted.");
LOG.info("Formatting...");
format(sd, nsInfo);
break;
default: // recovery part is common
sd.doRecover(curState);
}
然后再进行事务处理。
for(int idx = 0; idx <getNumStorageDirs(); idx++) {
doTransition(getStorageDir(idx), nsInfo, startOpt);
assert this.getLayoutVersion()== nsInfo.getLayoutVersion() :
"Data-node andname-node layout versions must be the same.";
assert this.getCTime() ==nsInfo.getCTime() :
"Data-node andname-node CTimes must be the same.";
}
回滚操作:将current命名为tmp,previousto current,再删除tmp目录。
升级操作:清空detach目录,删除previous目录,将current命名为tmp目录,
将finalized 和 RBW状态的数据在current目录里创建硬链接指向tmp目录中的数据块文件。
最后在将tmp目录命名为previous目录。
再将本次启动的事务信息写入到文件中,如版本号之类的。
this.writeAll();
FSDataset
副本集合的管理类。
副本列表(数据块列表)。
ReplicasMap volumeMap = new ReplicasMap();
FSDataset()
由DN构造函数时调用该类构造函数,用于实始化DN中的内部结构。
调用FSVolumeSet类中的方法getVolumeMap(),将相应的副本信息读入到内存中,即副本队列中。扫描多个数据目录,以及目录下的各个子目录,子文件。
voidgetVolumeMap(ReplicasMap volumeMap) throws IOException {
// add finalizedreplicas
dataDir.getVolumeMap(volumeMap,this);
// add rbw replicas
addToReplicasMap(volumeMap, rbwDir, false);
}
最后创建一个磁盘异步服务类FSDatasetAsyncDiskService。
File[] roots = newFile[storage.getNumStorageDirs()];
for (int idx = 0; idx <storage.getNumStorageDirs(); idx++) {
roots[idx] =storage.getStorageDir(idx).getCurrentDir();
}
asyncDiskService = newFSDatasetAsyncDiskService(roots);
FSDatasetAsyncDiskService
为每个数据块目录创建一个线程池,并用作为一个线程组,池的最小值为1,最大值为4,当前版本,只有delete操作会将任务经过线程池调度来进行异步处理,读写操作都是调文件操作同步去执行的。
FSDatasetAsyncDiskService(File[] volumes) {
threadFactory = new ThreadFactory() {
public ThreadnewThread(Runnable r) {
return new Thread(threadGroup, r);
}
};
// Create oneThreadPool per volume
for (int v = 0 ; v <volumes.length; v++) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_THREADS_PER_VOLUME, MAXIMUM_THREADS_PER_VOLUME,
THREADS_KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
newLinkedBlockingQueue<Runnable>(), threadFactory);
// This can reducethe number of running threads
executor.allowCoreThreadTimeOut(true);
executors.put(volumes[v],executor);
}
最后来一张副本的状态变化图
想要知道更多的细节,看源代码是最清楚的,同时自已可以调试运行代码。
- DataNode的分析
- datanode进程的分析(一)
- Hadoop-DataNode的相关分析(1)
- Hadoop源码分析之读文件时NameNode和DataNode的处理过程 选取datanode详解
- hadoop datanode源码分析
- hadoop datanode源码分析
- hadoop datanode源码分析
- HDFS datanode源码分析
- DataNode之BlockSender分析
- DataNode之DirectoryScanner分析
- Hadoop源码分析之DataNode的启动与停止
- Hadoop源码分析之DataNode的启动与停止
- Hadoop源码分析DataNode类
- DataNode启动流程源码分析
- HDFS------datanode的初始化
- HDFS------datanode的初始化
- NameNode生成DataNode的网络拓扑图的源码分析---NetworkTopology
- 关于DataNode更改IP地址后所可能引发HDFS集群状态变化的分析
- JSON 简介
- 第三章 标准库类型
- JSON 简介
- Java开发环境的搭建
- ANT使用
- DataNode的分析
- android Service--服务
- 心情
- csdn积分问题
- Building Qt with OpenGL ES accelerated by SGX
- Linux设备模型之热插拔、mdev 与 firmware
- 关于JNLP的几点备忘录
- JSP 注册表单,检测用户名是否存在(刚学习、原始的方法),没有采用AJAX
- フィールドより関連のストアドの抽出