HDFS源码分析心跳汇报之数据块汇报
在《HDFS源码分析心跳汇报之数据块增量汇报》一文中,我们详细介绍了数据块增量汇报的内容,了解到它是时间间隔更长的正常数据块汇报周期内一个smaller的数据块汇报,它负责将DataNode上数据块的变化情况及时汇报给NameNode。那么,时间间隔更长的正常数据块汇报都做了些什么呢?本文,我们将开始研究下时间间隔更长的正常数据块汇报。
首先,看下正常数据块汇报是如何发起的?我们先看下BPServiceActor工作线程的offerService()方法:
- /**
- * Main loop for each BP thread. Run until shutdown,
- * forever calling remote NameNode functions.
- */
- private void offerService() throws Exception {
- //
- // Now loop for a long time....
- //
- while (shouldRun()) {// 又是一个利用shouldRun()判断的while循环
- try {
- // 省略部分代码
- ...
- // 调用blockReport()方法,进行数据块汇报,放返回来自名字节点NameNode的相关命令cmds
- List<DatanodeCommand> cmds = blockReport();
- // 调用processCommand()方法处理来自名字节点NameNode的相关命令cmds
- processCommand(cmds == null ? null : cmds.toArray(new DatanodeCommand[cmds.size()]));
- // 省略部分代码
- //
- // There is no work to do; sleep until hearbeat timer elapses,
- // or work arrives, and then iterate again.
- // 计算等待时间waitTime:心跳时间间隔减去上次心跳后截至到现在已过去的时间
- long waitTime = dnConf.heartBeatInterval -
- (Time.now() - lastHeartbeat);
- synchronized(pendingIncrementalBRperStorage) {
- if (waitTime > 0 && !sendImmediateIBR) {// 如果等待时间大于0,且不是立即发送数据块增量汇报
- try {
- // 利用pendingIncrementalBRperStorage进行等待,并加synchronized关键字进行同步
- pendingIncrementalBRperStorage.wait(waitTime);
- } catch (InterruptedException ie) {
- LOG.warn("BPOfferService for " + this + " interrupted");
- }
- }
- } // synchronized
- } catch(RemoteException re) {
- <pre name="code" class="java"> // 省略部分代码
} catch (IOException e) {
- // 省略部分代码
} } // while (shouldRun())
可以看出,在BPServiceActor工作线程offerService()方法的while循环内,数据块汇报blockReport()方法执行时,仅有下面的waitTime的等待时间,其他情况下都是立即执行的。那么等待时间waitTime是如何计算的呢?它就是心跳时间间隔减去上次心跳后截至到现在已过去的时间,并且,如果等待时间waitTime大于0,且不是立即发送数据块增量汇报(标志位sendImmediateIBR为false),那么才会利用pendingIncrementalBRperStorage进行等待,并加synchronized关键字进行同步。在这里,我们就可以大胆猜测,数据块汇报的时间间隔应该是大于心跳时间间隔的,并且两者之间的距离肯定不小。
那么,我们开始研究实现正常数据块汇报的blockReport()方法吧,代码如下:
- /**
- * Report the list blocks to the Namenode
- * @return DatanodeCommands returned by the NN. May be null.
- * @throws IOException
- */
- List<DatanodeCommand> blockReport() throws IOException {
- // send block report if timer has expired.
- // 到期就发送数据块汇报
- // 取当前开始时间startTime
- final long startTime = now();
- // 如果当前时间startTime减去上次数据块汇报时间小于数据节点配置的数据块汇报时间间隔的话,直接返回null,
- // 数据节点配置的数据块汇报时间间隔取参数dfs.blockreport.intervalMsec,参数未配置的话默认为6小时
- if (startTime - lastBlockReport <= dnConf.blockReportInterval) {
- return null;
- }
- // 构造数据节点命令ArrayList列表cmds,存储数据块汇报返回的命令DatanodeCommand
- ArrayList<DatanodeCommand> cmds = new ArrayList<DatanodeCommand>();
- // Flush any block information that precedes the block report. Otherwise
- // we have a chance that we will miss the delHint information
- // or we will report an RBW replica after the BlockReport already reports
- // a FINALIZED one.
- // 调用reportReceivedDeletedBlocks()方法发送数据块增量汇报
- reportReceivedDeletedBlocks();
- // 记录上次数据块增量汇报时间lastDeletedReport
- lastDeletedReport = startTime;
- // 设置数据块汇报起始时间brCreateStartTime为当前时间
- long brCreateStartTime = now();
- // 从数据节点DataNode根据线程对应块池ID获取数据块汇报集合perVolumeBlockLists,
- // key为数据节点存储DatanodeStorage,value为数据节点存储所包含的Long类数据块数组BlockListAsLongs
- Map<DatanodeStorage, BlockListAsLongs> perVolumeBlockLists =
- dn.getFSDataset().getBlockReports(bpos.getBlockPoolId());
- // Convert the reports to the format expected by the NN.
- int i = 0;
- int totalBlockCount = 0;
- // 创建数据块汇报数组StorageBlockReport,大小为上述perVolumeBlockLists的大小
- StorageBlockReport reports[] =
- new StorageBlockReport[perVolumeBlockLists.size()];
- // 遍历perVolumeBlockLists
- for(Map.Entry<DatanodeStorage, BlockListAsLongs> kvPair : perVolumeBlockLists.entrySet()) {
- // 取出value:BlockListAsLongs
- BlockListAsLongs blockList = kvPair.getValue();
- // 将BlockListAsLongs封装成StorageBlockReport加入数据块汇报数组reports,
- // StorageBlockReport包含数据节点存储DatanodeStorage和其上数据块数组
- reports[i++] = new StorageBlockReport(
- kvPair.getKey(), blockList.getBlockListAsLongs());
- // 累加数据块数目totalBlockCount
- totalBlockCount += blockList.getNumberOfBlocks();
- }
- // Send the reports to the NN.
- int numReportsSent;
- long brSendStartTime = now();
- // 根据数据块总数目判断是否需要多次发送消息
- if (totalBlockCount < dnConf.blockReportSplitThreshold) {// 如果数据块总数目在split阈值之下,则将所有的数据块汇报信息放在一个消息中发送
- // split阈值取参数dfs.blockreport.split.threshold,参数未配置的话默认为1000*1000
- // Below split threshold, send all reports in a single message.
- // 发送的数据块汇报消息数numReportsSent设置为1
- numReportsSent = 1;
- // 通过NameNode代理bpNamenode的blockReport()方法向NameNode发送数据块汇报信息
- DatanodeCommand cmd =
- bpNamenode.blockReport(bpRegistration, bpos.getBlockPoolId(), reports);
- // 将数据块汇报后返回的命令cmd加入到命令列表cmds
- if (cmd != null) {
- cmds.add(cmd);
- }
- } else {
- // Send one block report per message.
- // 发送的数据块汇报消息数numReportsSent设置为1
- numReportsSent = i;
- // 遍历reports,取出每个StorageBlockReport
- for (StorageBlockReport report : reports) {
- StorageBlockReport singleReport[] = { report };
- // 通过NameNode代理bpNamenode的blockReport()方法向NameNode发送数据块汇报信息
- DatanodeCommand cmd = bpNamenode.blockReport(
- bpRegistration, bpos.getBlockPoolId(), singleReport);
- // 将数据块汇报后返回的命令cmd加入到命令列表cmds
- if (cmd != null) {
- cmds.add(cmd);
- }
- }
- }
- // Log the block report processing stats from Datanode perspective
- // 计算数据块汇报耗时并记录在日志Log、数据节点Metrics指标体系中
- long brSendCost = now() - brSendStartTime;
- long brCreateCost = brSendStartTime - brCreateStartTime;
- dn.getMetrics().addBlockReport(brSendCost);
- LOG.info("Sent " + numReportsSent + " blockreports " + totalBlockCount +
- " blocks total. Took " + brCreateCost +
- " msec to generate and " + brSendCost +
- " msecs for RPC and NN processing. " +
- " Got back commands " +
- (cmds.size() == 0 ? "none" : Joiner.on("; ").join(cmds)));
- // 调用scheduleNextBlockReport()方法,调度下一次数据块汇报
- scheduleNextBlockReport(startTime);
- // 返回命令cmds
- return cmds.size() == 0 ? null : cmds;
- }
数据块汇报的blockReport()方法处理流程大体如下:
1、取当前开始时间startTime;
2、如果当前时间startTime减去上次数据块汇报时间小于数据节点配置的数据块汇报时间间隔的话,直接返回null:
数据节点配置的数据块汇报时间间隔取参数dfs.blockreport.intervalMsec,参数未配置的话默认为6小时;
3、构造数据节点命令ArrayList列表cmds,存储数据块汇报返回的命令DatanodeCommand;
4、调用reportReceivedDeletedBlocks()方法发送数据块增量汇报;
5、记录上次数据块增量汇报时间lastDeletedReport;
6、设置数据块汇报起始时间brCreateStartTime为当前时间;
7、从数据节点DataNode根据线程对应块池ID获取数据块汇报集合perVolumeBlockLists:
key为数据节点存储DatanodeStorage,value为数据节点存储所包含的Long类数据块数组BlockListAsLongs;
8、创建数据块汇报数组StorageBlockReport,大小为上述perVolumeBlockLists的大小;
9、取出value:BlockListAsLongs:
9.1、取出value:BlockListAsLongs;
9.2、将BlockListAsLongs封装成StorageBlockReport加入数据块汇报数组reports,StorageBlockReport包含数据节点存储DatanodeStorage和其上数据块数组;
9.3、累加数据块数目totalBlockCount;
10、根据数据块总数目判断是否需要多次发送消息:
10.1、如果数据块总数目在split阈值之下,则将所有的数据块汇报信息放在一个消息中发送(split阈值取参数dfs.blockreport.split.threshold,参数未配置的话默认为1000*1000):
10.1.1、发送的数据块汇报消息数numReportsSent设置为1;
10.1.2、通过NameNode代理bpNamenode的blockReport()方法向NameNode发送数据块汇报信息;
10.1.3、将数据块汇报后返回的命令cmd加入到命令列表cmds;
10.2、如果数据块总数目在split阈值之上,将数据块汇报按照DatanodeStorage分多个消息来发送:
10.2.1、发送的数据块汇报消息数numReportsSent设置为i,即DatanodeStorage数目;
10.2.2、遍历reports,取出每个StorageBlockReport:
10.2.2.1、通过NameNode代理bpNamenode的blockReport()方法向NameNode发送数据块汇报信息;
10.2.2.2、将数据块汇报后返回的命令cmd加入到命令列表cmds;
11、计算数据块汇报耗时并记录在日志Log、数据节点Metrics指标体系中;
12、调用scheduleNextBlockReport()方法,调度下一次数据块汇报;
13、返回命令cmds。
HDFS源码分析心跳汇报之数据块汇报的更多相关文章
- HDFS源码分析心跳汇报之数据块增量汇报
在<HDFS源码分析心跳汇报之BPServiceActor工作线程运行流程>一文中,我们详细了解了数据节点DataNode周期性发送心跳给名字节点NameNode的BPServiceAct ...
- HDFS源码分析心跳汇报之周期性心跳
HDFS源码分析心跳汇报之周期性心跳,近期推出!
- HDFS源码分析心跳汇报之DataNode注册
HDFS源码分析心跳汇报之DataNode注册,近期推出!
- HDFS源码分析心跳汇报之BPServiceActor工作线程运行流程
在<HDFS源码分析心跳汇报之数据结构初始化>一文中,我们了解到HDFS心跳相关的BlockPoolManager.BPOfferService.BPServiceActor三者之间的关系 ...
- HDFS源码分析心跳汇报之数据结构初始化
在<HDFS源码分析心跳汇报之整体结构>一文中,我们详细了解了HDFS中关于心跳的整体结构,知道了BlockPoolManager.BPOfferService和BPServiceActo ...
- HDFS源码分析心跳汇报之整体结构
我们知道,HDFS全称是Hadoop Distribute FileSystem,即Hadoop分布式文件系统.既然它是一个分布式文件系统,那么肯定存在很多物理节点,而这其中,就会有主从节点之分.在H ...
- HDFS源码分析数据块汇报之损坏数据块检测checkReplicaCorrupt()
无论是第一次,还是之后的每次数据块汇报,名字名字节点都会对汇报上来的数据块进行检测,看看其是否为损坏的数据块.那么,损坏数据块是如何被检测的呢?本文,我们将研究下损坏数据块检测的checkReplic ...
- HDFS源码分析数据块校验之DataBlockScanner
DataBlockScanner是运行在数据节点DataNode上的一个后台线程.它为所有的块池管理块扫描.针对每个块池,一个BlockPoolSliceScanner对象将会被创建,其运行在一个单独 ...
- HDFS源码分析数据块复制监控线程ReplicationMonitor(一)
ReplicationMonitor是HDFS中关于数据块复制的监控线程,它的主要作用就是计算DataNode工作,并将复制请求超时的块重新加入到待调度队列.其定义及作为线程核心的run()方法如下: ...
随机推荐
- FCKeditor2.6.4控件及其使用范例
原文发布时间为:2009-06-14 -- 来源于本人的百度文章 [由搬家工具导入] 已经精简了,很小,不多说了,我的源代码去下载看。 web.config 看看。。。还有bin里面的dll就是控件, ...
- Codeforces Round #449 Div. 2 A B C (暂时)
A. Scarborough Fair 题意 对给定的长度为\(n\)的字符串进行\(m\)次操作,每次将一段区间内的某一个字符替换成另一个字符. 思路 直接模拟 Code #include < ...
- C 语言 和 python 调用 .so 文件
什么是静态库和动态库, 看一篇博客 http://www.cnblogs.com/skynet/p/3372855.html 现在,我们首先生成.so文件 首先, 我们写一个a.c文件 1 2 3 4 ...
- fastboot flash image usage
// // power on mobile phone // power on mobile phone // // reboot bootloader // android/out/host/lin ...
- c# 扩展LINQ的order by函数支持通过字符串来指定列名并支持多列
本文借鉴了https://blog.csdn.net/lan_liang/article/details/68523451. 将字符串转换为orderby的linq可以极大地减少重复劳动,可是该怎样将 ...
- 大话Spark(4)-一文理解MapReduce Shuffle和Spark Shuffle
Shuffle本意是 混洗, 洗牌的意思, 在MapReduce过程中需要各节点上同一类数据汇集到某一节点进行计算,把这些分布在不同节点的数据按照一定的规则聚集到一起的过程成为Shuffle. 在Ha ...
- PHP message: PHP Fatal error: require(): Failed opening required
PHP message: PHP Warning: require(/data/wwwroot/blog.sgfoot.com/bootstrap/autoload.php): failed to o ...
- Delphi中@,^,#,$分别表示什么?
@:取址运算符; var int:integer; p:^integer; new(P); int:=24; p:=@int; dispose(P); ^:指针的引用解析操作符; var pint:^ ...
- iOS github大全 & iOS7的学习blog
iOS github大全 :有600多个iOS各方面的开源库,并分类了 一天天学习iOS7 :每天学习一点iOS7的新特性
- 了解什么是WebLogic Server 多数据源(Multi-DataSource)
1. 什么是多数据源 我们知道配置WebLogic Server集群时一定要配置一个单一接入点(例如:Apache或F5),这样客户端只要访问这个单一入口点就可以了.对于客户来说,就好象访问一台服 ...