给公司研发部门培训的HDFS的PPT文档及问题答疑

之前的一篇博客说到,公司请的大数据培训师那简直一个差啊,就差直接说自己是骗子。忍无可忍之下,搭建了个环境,简单跑了一下,然后把HDFS的主要代码翻了一遍。不得不说,设计的真不错。不过据说当年的初期的版本代码啥的也很烂。

 

写了两份关于HDFS的培训文档,给研发部门培训了下。后面原准备接着来两次mapreduce和spark的培训,因为种种原因,一直耽搁下来。HDFS培训完针对同学提出的问题结合源码给出答疑,培训文档附件给出,这里把同学们提的问题以及我给出的解答贴出来。

 

1. 如果在一个数据节点写的过程中出现问题,那么容错处理是怎样的

对比从HDFS读文件,写文件过程比较复杂,有三个地方会影响写的过程,也就是说需要有容错的设计。在这次sharing,我们知道,写数据需要首先建立一个管道(pipeline),如果忘记了,可以看下面的图回忆一下,管道里是要写入当前块(block)的三个数据节点(DataNode).  具体点的话,可以分成下面两个部分:

 

写请求(write packet)

1) 客户端并不是一开始就把数据(block)直接写入管道,而是先测试管道是否能正确建立。如果在建立管道的过程中,有某个数据节点不能正常工作,客户端将终止这个管道,然后跟名字节点通信,获取另外三个数据节点,尝试建立一个新的管道,这样的尝试有3次(可以配置),如果均失败,则此次写文件失败。如果成功,则管道成功建立,留给后续写数据用

 

2) 如果管道建立成功,客户端开始写数据。客户端首先把数据传送给管道里的第一个数据节点,这个节点拿到数据后,首先将数据写往下一个数据节点,然后开始往本地写。这里有两个可能出错的地方:

a. 往下个数据节点写的时候可能出错这种情况的容错处理,和建立管道的错误处理是一样的,因为都是返回的TCP管道的错误码。而且,都会告诉客户端是那个数据节点出的错误。很重要的一点是,处理错误响应的是另外一个线程(PacketResponder)。注意,这种情况往本地写数据有可能是成功的。一旦成功,数据节点会和名字节点直接通信来声明本地写成功。

b. 往本地写数据的过程中,可能出错这种情况下当前,数据节点会直接kill掉上面提到的响应处理的线程(PacketResponder),这样造成的结果就是当前数据节点前面的那个节点收不到任何的响应,那么前一个节点就知道,当前这个节点出了问题,就把它标识成bad,然后返回给客户端。 

 

返回响应(ack packet)

1) 建立管道的时候,响应需要顺着管道逆向返回给客户端,如果其中任何一个节点有问题了,则错误会被它前面的节点捕获,错误代码会返回给客户端。

2) 写数据的时候,响应是在另外的线程里处理,处理的策略和1)类似。
 

2. mapreduce函数的值是怎么获取的

大多数情况是不用去实现自己的FileInputFormat(比如WordCount的例子,mapreduce函数里的值(每行的文本)就是Hadoop给自动分析和设置进去),因为Hadoop已经有一些默认的实现,当既有的那些满足不了你的需求的时候,可以通过继承FileInputFormat来实现自己的。



3. 是否可以用HDFSAPI来直接获取文件的某一行记录

首先,通过API直接获取某一个行的记录,肯定是不行的,注意我说的是通过API直接获取。那么如果我想要某一个行的值,是否是可以?这个肯定是可以的。因为我们能通过HadoopAPI来获取这个文件的输出流,那么在这个基础上,可以得到我们想要的那一行,但是需要我们自己写代码。看下面的代码实现

try {

     fs = FileSystem.get(conf);

     in = fs.open(path);

     reader = new BufferedReader(new InputStreamReader(in));

     String line = null;

     while((line = reader.readLine()) != null) {

          System.out.println("Record: " + line);

     }

} catch (IOException e) {}

 

4. 比如我们有1 -> 2 -> 3 -> 4的写入过程,

a. 它是多线程的吗?比如2 3 写,同时也在另一个线程里往2的本地写

b. 如果往3写到一半的时候,3挂掉了,3这时候存在了一个坏的数据块,它挂掉以前写正确的部分,它怎么处理的?全部放弃吗?这个时候2怎么处理它已经成功写到本地的部分?

 

1)是多线程的。针对每一个写入packet的请求,数据节点都会开启一个线程去响应。但是有个线程总数的限制,默认是256,可以在配置文件指定。23里写和本地写是在同一个线程里。

 

2) 如果3写到一半挂掉的话,3确实存在一个没有写完的数据块。按照我的第一封mail里解释的,当前管道会中断,返回错误信息给HDFS客户端,并且包含出错的节点。需要注意的是,这个时候,各个数据节点不做任何处理,目前是简化复杂性。客户端收到错误信息以后,按照前面所说的,它知道第3个节点出错了,所以,就把这个节点去掉,选取剩下3个节点中的一个数据节点作为主节点,然后重新生成一个管道。进行数据恢复。一旦数据恢复成功,有效的数据节点上的块的版本号会更新。对于挂掉的节点3,当它向名字节点周期汇报(heartbeat)的时候,名字节点会命令它删除出错的块,因为版本信息不一样了

 

对于2或者其它正常的节点,本地写入的进度可能是有差异的,那么HDFS客户端会发出请求到各个正常的节点,来得到目前每个节点写入到什么程度,这里有两种策略:完整恢复和以最小写入的那个点为基础来恢复

数据正常写入的时候用的是流式接口,实现是用的TCPsocket,而出错时候的恢复,则使用的IPC接口,你可以理解是RPC

 

稍微贴点代码帮助理解我上面第二点解释的:

a.删除出错的节点,取出剩下的节点

System.arraycopy(nodes, 0, newnodes, 0, errorIndex);

System.arraycopy(nodes, errorIndex+1, newnodes, errorIndex, newnodes.length-errorIndex);

 

b. 选取一个好的节点作为主节点,并发起IPCRPC)连接

primaryNode = Collections.min(Arrays.asList(newnodes));

primary = createClientDatanodeProtocolProxy(primaryNode, conf, block, accessToken, recoveryTimeout, connectToDnViaHostname);

 

5. block和meta文件名的数字代表什么意思



 blk是前缀, 数字是随机产生的。看下面的源代码

 

private Block allocateBlock(String src, INode[] inodes) throws IOException {

    Block b = new Block(FSNamesystem.randBlockId.nextLong(), 0, 0);

    while(isValidBlock(b)) {

      b.setBlockId(FSNamesystem.randBlockId.nextLong());

    }

    b.setGenerationStamp(getGenerationStamp());

    b = dir.addBlock(src, inodes, b);

    NameNode.stateChangeLog.info("BLOCK* allocateBlock: "

                                 +src+ ". "+b);

    return b;

  }

对meta文件来说,文件名的组成是block id + number.

 

public static final String METADATA_EXTENSION = ".meta";

  static String getMetaFileName(String blockFileName, long genStamp) {

    return blockFileName + "_" + genStamp + METADATA_EXTENSION;

  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值