Hadoop(三)

本文详细介绍了HadoopHDFS的写流程,包括RPC调用、数据切分、pipeline构建和错误处理策略,以及主备切换过程中的应对措施。还讨论了ACK校验、Entry对象和package的构成,以及HDFS读流程的细节。此外,文章还涵盖了Hadoop的脑裂问题及其解决方案和邦联机制的概念。
摘要由CSDN通过智能技术生成

Hadoop_Day03


HDFS写流程

  1. client客户端与namenode之间建立PRC连接,客户端发起上传文件的请求,namenode会去做三个校验

    • 用户是否有写入权限
    • 文件是否已经存在
    • 磁盘空间大小是否足够

    上述三个条件都满足的话namenode会针对这个文件创建一个空的Entry(用来存放文件—block块—bloc块所在的)对象,并返回成功状态给client(DFS)

    RPC(远程过程调用)是一种计算机通信协议,它允许一个计算机程序通过网络请求调用另一个计算机程序的函数或过程,并接收返回结果,使得像本地调用一样方便地实现跨网络的远程调用。

  2. client接受到处理成功的状态之后会创建一个FSDataOutputStream给客户端使用,此时客户端对文件进行切分操作形成block块,接着客户端向namenode询问第一个block块存放的位置,namenode根据网络拓扑关系,机架感知原理和副本机制找到可以上传的datanode返回给客户端(如A,B,C三台)

  3. client通过RPC调用与A建立起一个pipline,A再去请求B,B再去请求C从而建立一个完整的pipline,然后逐级返回client

  4. client先从磁盘中读取数据到本地内存缓存开始往A上传第⼀个block,以packet为单位 (默认64K),A收到⼀个 packet 就会传给B,B传给C,数据被分割成⼀个个 packet数据包在 pipeline 上依次传输,在 pipeline 反⽅向上,逐个发送ack校验,当⼀个 block 传输完成之后,client再次请求 NameNode返回第二个block应该上传到那些datanode上。

    注:

    FSDataOutputStream被封装成DFSOutputStream.DFSOutputStream可以协调namenode和datanode。客户端开始写数据到DFSOutputStream,DFSOutputStream会把数据切成一个个小packet,然后排成队列data quene。DataStreamer会去处理接受data queue,他先问询namenode这个新的block最适合存储的在哪几个datanode里,比如副本数是3,那么就找到3个最适合的datanode,把他们排成一个pipeline.DataStreamer把packet按队列输出到管道的第一个datanode中,第一个datanode又把packet输出到第二个datanode中,以此类推。

    DFSOutputStream还有一个对列叫ack queue,也是有packet组成,等待datanode的收到响应,当pipeline中的所有datanode都表示已经收到的时候,这时akc queue才会把对应的packet包移除掉。

    如果在写的过程中某个datanode发生错误,会采取以下几步:1) pipeline被关闭掉;2)为了防止丢包ack queue里的packet会同步到data queue里;3)把产生错误的datanode上当前在写但未完成的block删掉;4)block剩下的部分被写到剩下的两个正常的datanode中;5)namenode找到另外的datanode去创建这个块的复制。当然,这些操作对客户端来说是无感知的。


写流程中突然断电的应对策略

  • pipeline被关闭掉
  • 为了防止丢包ack queue里的packet会同步到data queue里
  • 把产生错误的datanode上当前在写但未完成的block删掉
  • block剩下的部分被写到剩下的两个正常的datanode中
  • namenode找到另外的datanode去创建这个块的复制

ACK校验的过程

  1. 客户端首先将一个 packet 发送给 node1, 同时给予 node1 一个 ack 状态
  2. node1接受数据后会将数据继续传递给 node2, 同时给予 node2 一个 ack 状态
  3. node2接受数据后会将数据继续传递给 node3, 同时给予 node3 一个 ack 状态
  4. node3将这个 packet 接受完成后,会响应这个 ack 给 node2 为 true
  5. node2会响应给 node1 , 同理 node1 响应给客户端

Entry对象什么时候开始记录三个映射关系的

是每一个block块发送成功,客户端接收到所有package的ack校验的时候


一个package的微观构成

  • 由PackageHeader以及PackageData构成

    • PackageHeader中重要的组成部分有两个,一个是offset in block,另一个是last package in block
    • PackageData中重要的组成部分有两个,一个是chunkdata,另一个是chunkchecksum,chunkchecksum=128:1,checksum:在数据处理和数据通信领域中,用于校验目的的一组数据项的和
      在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述


HDFS读流程

  1. client向NameNode发送RPC的请求,namenode会去做两个校验

    • 用户是否有读取权限
    • 文件是否存在

    如果满足上述的两个条件,namenode会视情况返回文件的部分或者全部block块的列表,对于每个block,namenode都会返回含有该Block块副本的datanode的地址

  2. 这些返回的datanode地址,会按照集群拓扑结构得出datanode与客户端的距离,然后进⾏排序,排序两个规则:⽹络拓扑结构中距离client近的排靠前;⼼跳机制中超时汇报的 DataNode状态为STALE,这样的排靠后

  3. client选取排序靠前的datanode来读取block,如果客户端本身就是datanode,那么将从本地直接获取数据;底层上本质是建⽴Socket Stream(FSDataInputStream),重复的调⽤⽗类DataInputStream的read⽅法,直到这个块上的数据读取完毕

  4. 当读完列表的block后,若⽂件读取还没有结束,客户端会继续向namenode获取下⼀批的block列表

  5. 读取完⼀个 block 都会进⾏checksum验证,如果读取DataNode时出现错误,客户端会通知NameNode,然后再从下⼀个拥有该 block副本的DataNode继续读。

  6. read ⽅法是并⾏的读取block信息,不是⼀块⼀块的读取;namenode只是返回client请求包含块的DataNode地址,并不是返回请求块的数据

  7. 最终读取来所有的block会合并成⼀个完整的最终⽂件。


在windows系统中模拟hadoop环境

将hadoop.dll文件移动到C:\Windows\System32目录下

然后在D盘的soft目录中新建一个hadoop目录然后再建一个bin目录,之后将winutils.exe文件放到bin目录中去,之后在环境变量中配置HADOOP_HOME,再再Path中添加bin目录即可


使用Java操作HDFS

package org.hadoop;


import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.fs.permission.FsPermission;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

public class HDFSDemo1 {
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS", "hdfs://master:9000");
        FileSystem fs = FileSystem.get(conf);

        //创建文件夹操作
//        Path path = new Path("/xlj/data");
//        boolean flag = fs.mkdirs(path);
//        if (flag) {
//            System.out.println("文件夹创建成功");
//        } else {
//            System.out.println("文件夹创建失败");
//        }

        //上传文件操作
//        Path path1 = new Path("src/main/data/students.txt");
//        Path path2 = new Path("/xlj/data");
//        fs.copyFromLocalFile(path1,path2);
//        System.out.println("文件上传成功");

        //下载文件操作
//        Path path11 = new Path("/xlj/data/a.txt");
//        Path path22 = new Path("src/main/data/a.txt");
//        fs.copyToLocalFile(path11, path22);
//        System.out.println("文件下载成功");

        //修改副本数
//        Path path = new Path("/xlj/data/students.txt");
//        short s = 1;
//        boolean b = fs.setReplication(path, s);
//        if (b) {
//            System.out.println("修改副本成功");
//        } else {
//            System.out.println("修改副本失败");
//        }

        //获取文件基本信息

//        Path path = new Path("/xlj/data/students.txt");
//        FileStatus fileStatus = fs.getFileStatus(path);
//        long accessTime = fileStatus.getAccessTime();
//        Date date = new Date(accessTime);
//        SimpleDateFormat sdf = new SimpleDateFormat();
//        String accessTimeFormat = sdf.format(date);
//        String group = fileStatus.getGroup();
//        long len = fileStatus.getLen();
//        long blockSize = fileStatus.getBlockSize();
//        String owner = fileStatus.getOwner();
//        long modificationTime = fileStatus.getModificationTime();
//        Date date1 = new Date(modificationTime);
//        String modificationTimeFormat = sdf.format(date1);
//        Path path1 = fileStatus.getPath();
//        FsPermission permission = fileStatus.getPermission();
//        System.out.println(accessTimeFormat + "\n"
//                + group + "\n"
//                + len + "\n"
//                + blockSize + "\n"
//                + owner + "\n"
//                + modificationTimeFormat + "\n"
//                + path1 + "\n"
//                + permission + "\n");

        //查看文件block块的信息
//        Path path = new Path("/xlj/data/students.txt");
//        FileStatus fileStatus = fs.getFileStatus(path);
//        BlockLocation[] fileBlockLocations = fs.getFileBlockLocations(path, 0, fileStatus.getLen());
//        for (BlockLocation fileBlockLocation : fileBlockLocations) {
//            String[] names = fileBlockLocation.getNames();
//            String[] hosts = fileBlockLocation.getHosts();
//            long offset = fileBlockLocation.getOffset();
//            System.out.println("主机名" + Arrays.toString(names) + '\n'
//                    + "用户名" + Arrays.toString(hosts) + '\n'
//                    + "偏移量" + offset);
//        }

        //使用对流复制的方法完成文件的传输
        String localPath = "src/main/data/b.txt";
        String hdfsPath = "/xlj/data/b.txt";

//        InputStream inputStream = Files.newInputStream(Paths.get(localPath));
//        FSDataOutputStream fsDataOutputStream = fs.create(new Path(hdfsPath));
//
//        IOUtils.copy(inputStream,fsDataOutputStream);
//
//        IOUtils.closeQuietly(inputStream);
//        IOUtils.closeQuietly(fsDataOutputStream);


        OutputStream outputStream = Files.newOutputStream(Paths.get(localPath));
        FSDataInputStream fsDataInputStream = fs.open(new Path(hdfsPath));

        IOUtils.copy(fsDataInputStream, outputStream);

        IOUtils.closeQuietly(fsDataInputStream);
        IOUtils.closeQuietly(outputStream);

        fs.close();

    }
}

Hadoop1.X带来的问题

  • 单点故障:每个集群在同一时刻只能有一个namenode,namenode所在的服务器奔溃,namenode所在的计算机上需要做软硬件升级,namenode随着业务增多内存沾满导致无法工作,等事件使得整个集群不可用
  • 水平扩展问题,将来服务器启动的时候,启动速度慢

HA设计思路

1、hadoop2.x启用了主备节点切换的模式(1主1备)

2、当主节点出现异常的时候,集群直接将备用节点切换成主节点,要求备用节点马上就要工作,主备节点内存几乎同步有

3、独立的线程对主备节点进行监控健康状态

4、需要有一定的选举机制,帮助我们确定主从关系

5、我们需要实时存储日志的中间件


HA中各个组件的作用

  • Active namenode : 作用与普通模式下的namenode一致

  • Standby namemode: 作用与普通模式下的namenode一致,且多加上了secondarynamenode的功能,可以进行日志的合并

  • JN集群:用于临时存放edits日志文件,只要有半数以上的JN节点接收到了该日志文件,就认为该日志文件有效,Standby namemode会去Quorum JournalNode Manager(共享存储系统)拉取日志文件(SNN上的日志有可能不是最新的)

  • DataNode:作用与普通模式下的datanode一致

  • FailOverController(故障转移控制器):

    • 启动时

      • 当ZKFC只检查到一个节点是健康状态,直接将其设置为主节点
        当zkfc检查到两个NN节点是的健康状态,发起投票机制,选出一个主节点,一个备用节点,并修改主备节点的状态
        
    • 运行时

      • 由 ZKFailoverController、HealthMonitor 和 ActiveStandbyElector 这 3 个组件来协同实现主备切换 
        
        a. ZKFailoverController启动的时候会创建 HealthMonitor 和 ActiveStandbyElector 这两 个主要的内部组件
        b. HealthMonitor 主要负责检测 NameNode 的健康
        c. ActiveStandbyElector主要负责完成自动的主备选举,内部封装了 Zookeeper 的处理逻辑 
        
  • zookeeper:

    • 为主备切换控制器提供主备选举支持和
    • ZKFC保持心跳机制确定ZKFC的存活

SNN为什么不直接去ANN去同步数据

通过使用JournalNode集群来持久化和同步数据,可以实现Hadoop的高可用性、数据一致性和并行处理能力,从而提供可靠稳定的分布式存储和计算服务。


主备切换过程

  • NameNode在选举成功后, ActiveStandbyElector会在zk上创建一个ActiveStandbyElectorLock临时节点,而没有选举成功的备 NameNode 中的ActiveStandbyElector 会监控这个节点

  • 如果Active NameNode对应的HealthMonitor检测到NameNode的状态异常时,ZKFailoverController会主动删除当前在Zookeeper上建立的临时节点ActiveStandbyElectorLock

  • 处于Standby状态的NameNode的ActiveStandbyElector 注册的监听器就会收到这个节点的NodeDeleted 事件并创建ActiveStandbyElectorLock临时节点,本来处于Standby状态的 NameNode 就选举Active NameNode并随后开始切换为Active状态。

  • 如果是 Active NameNode 的机器整个宕掉的话,那么跟 zookeeper 连接的客户端线程也挂了 , 会话结束 , 那么根据 Zookeepe 的临时节点特性, ActiveStandbyElectorLock 节点会自动被删除,从而也会自动进行一次主备切换

    在这里插入图片描述


脑裂的定义

脑裂是Hadoop2.X版本后出现的全新问题,实际运行过程中很有可能出现两个namenode同时服务于整个集群的情况,这种情况称之为脑裂


出现脑裂的原因

网络延迟、心跳故障、设备故障


脑裂的解决方案

  • 规定共享存系统任一时刻,只有一个NN可以写入
  • Client需要保证同一时刻只有一个NN能够对Client的请求发出正确的响应
  • 每个NN改变状态的时候,向DN发送自己的状态和一个本次选举对应的序列号。DN在运行过程中维护此序列号,当 failover 时,新的NN在返回DN心跳时会返回自己的active状态和一个更大的序列号。DN 接收到这个返回是认为该 NN 为新的 active 。如果这时原来的active恢复,返回给 DN 的心跳信息包含 active 状态和原来的序列号,这时 DN 就会拒绝这个 NN 的命令。
  • ActiveStandbyElector为了实现 fencing ,当NN成为ANN之后创建 Zookeeper临时节点ActiveStandbyElectorLock ,创建 ActiveBreadCrumb的持久节点,这个节点里面保存了这个Active NameNode 的地址信息 (node-01)Active NameNode 的 ActiveStandbyElector 在正常的状态下关闭Zookeeper Session的时候,会一起删除这个持久节点,但如果 ActiveStandbyElector 在异常的状态下关闭,那么由于ActiveBreadCrumb 是持久节点,会一直保留下来,后面当另一个NameNode 选主成功之后,会注意到上一个Active NameNode遗留下来的这个节点,从而会回调ZKFailoverController 的方法对旧的Active NameNode进行fencing
  • sshfence
  • shellfence

什么是hadoop的邦联机制

Hadoop的邦联机制是指将多个独立的Hadoop集群连接在一起,形成一个逻辑上统一的大规模集群,以共享和处理跨集群的数据和计算任务

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值