HDFS pipeline写 -- 客户端
上一篇说了datanode端如何处理pipeline写请求的,这里主要看DFSClient。
这里以append为例, write差不多。
创建一个pipeline用于append操作的流程:
FileSystem.get(configuration) 返回一个已经初始化完成的DistributedFileSystem对象,内部包含一个DFSClient对象
DistributedFileSystem.append(Path)内部调用DFSClient的append方法返回一个FSDataOutputStream(实际是HdfsDataOutputStream)
,应用往这个stream中write数据即可。
看看DFSClient.append()做了什么
调namenode.getFileInfo(file)向namenode得到file的相关信息,包括文件长度,
mtime,atime,block_size,是否为目录,权限,owner,group等.调namenode.append(file,clientName)获取file最后一个block的位置信息,以LocatedBlock形式返回.具体看看这个函数.
2.1 namenode内部调用FSNameSystem的appendFile(file, clientName)
从FSDirectory中拿到file对应的INode,做一些check,比如权限,file是文件,不是目录等。
然后调用recoverLeaseInternal检查是否file需要recover lease,详细看看这个函数:
从INodeFile可以知道这个文件是否处于under construction状态,如果不是,则说明不需要进 行lease recovery. 如果是,那么从LeaseManager拿到clientname对应的Lease,以及file所在的Lease,检查是否这个file的Lease已经被当前clientname持有,如果是,则抛异常,客户端append失败。否则,从INodeFile中拿到当前file的lease holder
,然后去LeaseManager中根据lease holder拿到Lease,然后判断Lease是否过期(lease最后更新时间离现在已经超过soft limit,默认60s)。如果没有过期,说明当前文件的lease正被别人持有,不能进行append,抛异常,客户端append失败.如果过期,开始
进入lease recovery阶段, 执行internalReleaseLease(),函数的目的就是使这个file的最后一个block的所有副本数据一致,副本数据不一致可能由之前写这个文件的客户端意外宕机导致。这个问题后续专门分析。2.2 检查最后一个block是否副本数足够,如果不足够,同样抛异常,客户端失败。
2.3 将INodeFile设置为under construction状态,将clientname作为lease holder设置到INodeFile中。同时将clientname,filename添加到Lease中。
2.4 从INodeFile拿出最后一个block的BlockInfo,如果block已经满了,则返回null,后续客户端会发addBlock请求给namenode申请block进行append。如果block不满,将最后一个block设置为under construction状态。更新blocksMap.最后,将最后一个block的LocatedBlock返回给DFSClient.
构造DFSOutputStream对象,并且启动对象内部的DataStreamer线程,这里会根据上一步的LocatedBlock来设置DataStreamer的stage状态,显然,如果上一步返回的LocatedBlock不为空,说明append的最后一个block没有满,则stage设置为PIPELINE_SETUP_APPEND,isAppend设置为true,如果LocatedBlock为空,则stage设置为PIPELINE_SETUP_CREATE,isAppend设置false,在这种情况下,会调用nextBlockOutputStream函数来建立pipeline,内部会调用addBlock()向namenode申请一个新的block用于append。最后返回一个包装了DFSOutputStream的HdfsDataOutputStream的对象给客户端.
客户端拿到HdfsDataOutputStream后,往流里写数据,数据以packet的形式被放如DataStreamer
的dataQueue中。DataStreamer线程就是建立pipeline后,往pipeline中写一个个packet,最后关闭pipeline.
对于append来说,建立pipeline的函数是setupPipelineForAppendOrRecovery,函数内部会处理
建立pipeline过程中的失败和重试,最后函数的返回值就代表pipeline是否建立成功.
下面看setupPipelineForAppendOrRecovery是如何建立pipeline和建立失败时pipeline的重试工作.
DFSOutputStream中的DataStreamer建立pipeline时,如果出现某个坏的datanode,那么pipeline会断掉,例如client->A->B->C,B给C建立pipeline时,C是一个坏的datanode,那么在B的DataXceiver.writeBlock()中会设置BlockOpResponseProto的firstBadLink,然后回复给A,A收到后,主动断开和B的连接,以此类推。
pipeline建立失败会将这个第一个坏的datanode踢出pipeline并且放入到一个失败集合中.然后对剩下的datanode检查是否满足了replace-datanode policy,简单来说,就是检查是否需要新增一个datanode进来构成新的pipeline集合,是否需要增加新的datanode进来,主要考虑几个因素, 副本数,是否执行的hflush,是否append操作,当前pipeline中datanode个数.如果最后判定需要新增加datanode,会调用getAdditionalDatanode请求namenode重新分配一个datanode,这里会将失败集合发给namenode,namenode这次不会再选择这些坏的datanode.随后调updateBlockForPipeline向namenode重新申请这个block的新的generation stamp和token.然后调用createBlockOutputStream()重新建立pipeline.一旦建立pipeline成功,会调用updatePipeline向namenode更新pipeline,将旧block和新block的信息发给namenode,namenode根据旧block信息从BlockManager中找到对应的BlockInfo,确定其处于UNDER_CONSTRUCTION状态,并且从BlockInfo中拿出这个Block所在的HDFS文件对应的inode结构INodeFile,并且确定file处于under construction状态,并且确定从INodeFile拿出的clientname和传进来的clientname是相等的。然后,从INodeFile中取出最后一个BlockInfo,将其cast成BlockInfoUnderConstruction,然后再次进行check,如果新block的generation stamp小于等于最后一个block的generation stamp,或者字节数小于最后一个block的generation stamp,那么抛异常.否则更新最后一个block的字节数和generation stamp为新BlockInfo的值,并且设置好最后一个block的List replicas属性,每个replica的状态设置为RBW.
pipeline 建立好后,DataStreamer启动ResponseProcessor线程用于处理下游对packet的ack,接着DataStreamer从dataQueue中取出一个个的packet往pipeline发.最后拆除pipeline.
参考资料
hadoop-hdfs-2.4.1.jar
HDFS pipeline写 -- 客户端的更多相关文章
- HDFS pipeline写 -- datanode
站在DataNode的视角,看看pipeline写的流程,本文不分析客户端部分,从客户端写数据之前拿到了3个可写的block位置说起. 每个datanode会创建一个线程DataXceiverServ ...
- 2 weekend110的HDFS的JAVA客户端编写 + filesystem设计思想总结
HDFS的JAVA客户端编写 现在,我们来玩玩,在linux系统里,玩eclipse 或者, 即,更改图标,成功 这个,别慌.重新换个版本就好,有错误出错是好事. http://www.eclips ...
- HDFS的写数据过程分析
HDFS的写数据过程分析 我们通过FileSystem类可以操控HDFS, 那我们就从这里开始分析写数据到HDFS的过程. 在我们向 HDFS 写文件的时候,调用的是 FileSystem.creat ...
- 一脸懵逼学习hadoop之HDFS的java客户端编写
1:eclipse创建一个项目,然后导入对应的jar包: 鼠标右击项目,点击properties或者alt+enter快捷键--->java build path--->libraries ...
- 一次失败的尝试hdfs的java客户端编写(在linux下使用eclipse)
一次失败的尝试hdfs的java客户端编写(在linux下使用eclipse) 给centOS安装图形界面 GNOME桌面环境 https://blog.csdn.net/wh211212/artic ...
- day03-hdfs的客户端操作\hdfs的java客户端编程
5.hdfs的客户端操作 客户端的理解 hdfs的客户端有多种形式: 1.网页形式 2.命令行形式 3.客户端在哪里运行,没有约束,只要运行客户端的机器能够跟hdfs集群联网 文件的切块大小和存储的副 ...
- Linux启动kettle及linux和windows中kettle往hdfs中写数据(3)
在xmanager中的xshell运行进入图形化界面 sh spoon.sh 新建一个job
- HDFS的Java客户端编写
总结: 之前在教材上看hdfs的Java客户端编写,只有关键代码,呵呵…….闲话不说,上正文. 1. Hadoop 的Java客户端编写建议在linux系统上开发 2. 可以使用eclipse,ide ...
- 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_5-9.使用JWT生成用户Token回写客户端
笔记 9.使用JWT生成用户Token回写客户端 简介:讲解用户授权登录后,需要生成登录凭证重定向到页面上 1.获取当前页面访问地址 2.根据User基本信息生成token 3.重定向到指定页 ...
随机推荐
- CGI PL PERL脚本 提权
windows 2003 下,安装ActivePerl-5.16.2.1602-MSWin32-x86-296513 IIS 添加CGI支持.并在对应网站配置下面,添加CGI后缀或PL后缀 与 对应的 ...
- 【jQuery源码】jQuery对象初始化
看了一下午还是有很多地方没弄明白,jQuery的一些工具方法的原理也不完全清楚,这篇文章会随着我深入阅读jQuery源码的同时不断更新. // Initialize a jQuery object / ...
- 开发工具 -- PyDev的使用
1.创建PyDev工程 2.创建源文件夹src 3.新建.py文件 其他注意事项 1.设置字体大小 2.显示行号Ctrl + F10 3.编码设置为UTF-8 4.最好将工程创建到默认工作空间 5.总 ...
- Go语言学习笔记十二: 范围(Range)
Go语言学习笔记十二: 范围(Range) rang这个关键字主要用来遍历数组,切片,通道或Map.在数组和切片中返回索引值,在Map中返回key. 这个特别像python的方式.不过写法上比较怪异使 ...
- [中英对照]Linux kernel coding style | Linux内核编码风格
Linux kernel coding style | Linux内核编码风格 This is a short document describing the preferred coding sty ...
- sql典例分析
1. 条件过滤 & Having 表结构 #tab_a #tab_b 表关系 tab_a.id = tab_b.relation_id 表数据 需求 查新把tab_a的ID对应的表tab_b的 ...
- Polymorphic form--多态表单
一个ruby on rails项目,用户和公司的模型都有地址. 我要创建一个地址表,包含用户和公司表的引用,比直接做下去要好一点,这回让我的数据库设计保持干净. 我的第一印象是,这似乎很难实现,外面所 ...
- Deep Residual Learning for Image Recognition(残差网络)
深度在神经网络中有及其重要的作用,但越深的网络越难训练. 随着深度的增加,从训练一开始,梯度消失或梯度爆炸就会阻止收敛,normalized initialization和intermediate n ...
- 【LeetCode题解】160_相交链表
目录 160_相交链表 描述 解法一:哈希表 思路 Java 实现 Python 实现 解法二:双指针(推荐) 思路 Java 实现 Python 实现 160_相交链表 描述 编写一个程序,找到两个 ...
- C# 在同一个项目里启动不同的类文件
比如有两个类文件分别为 Person.cs 和 Enum.cs : using System; using person; namespace HelloWorld { class HelloWorl ...