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()方法如下: ...
随机推荐
- 【BZOJ1030】文本生成器(容斥原理,AC自动机,计数DP)
题意:给出n个字符串,求长为m至少包含n个里其中一个的串的字符串一共有多少个,字符集为A到Z,答案对10007取模 n<=60,len<=100 思路:将至少一个转化为所有个数减去没有出现 ...
- SQLite-FMDatabase用法
FMDatabase用法 转载 http://blog.sina.com.cn/s/blog_94d94f1a01015gcr.html 以下是FMDB的一些基本使用,FMDB框架其实只是一层很薄的封 ...
- 代码怎样重构<1>
原文发布时间为:2011-05-24 -- 来源于本人的百度文章 [由搬家工具导入]
- 改变querystring值,然后重定向
原文发布时间为:2009-11-13 -- 来源于本人的百度文章 [由搬家工具导入] 本页面改变querystring值,然后重定向 本页面,避免出现重复querystring。。 如避免出现 www ...
- Android 单击图片切换效果
新建一个Android项目,命名为FrameLayout 此实例主要操作src文件夹下的MainActivity.Java类文件和res/layout下的activity_main.xml布局文件 1 ...
- Node.js应用场景及发展趋势
node主要应用场景是在大前端,阿里的思路是比较合适的,但是必须要注意,绝对不能让node做太多的业务逻辑,他只适合接受人家生成好的数据,然后或渲染后,或直接发送到客户端.如果让node做复杂的业务逻 ...
- 连接mysql
1.nuget 所搜MySql.Data 2.appsettings.json { "ConnectionStrings": { "DefaultConnection& ...
- 洛谷 P1372 又是毕业季I[数论/神坑规律题]
题目描述 为了把毕业晚会办得更好,老师想要挑出默契程度最大的k个人参与毕业晚会彩排.可是如何挑呢?老师列出全班同学的号数1,2,……,n,并且相信k个人的默契程度便是他们的最大公约数(这不是迷信哦~) ...
- Linux Redhat7 开机启动python脚本
cd /usr/lib/systemd/system touch proxy.service ##################################################### ...
- Jenkins配置MSBuild实现自动部署2(项目实践)
继上一篇文章http://www.cnblogs.com/EasonJim/p/6077225.html,大致实现的思路,今天来记录一个真实项目实践. 一.新建项目 选择[构建一个自由风格的软件项目] ...