HDFS源码解析

Hadoop分布式文件系统(HDFS)是Hadoop生态系统的核心组件之一,它是一个可扩展的分布式文件系统,用于存储大量数据。本文将对HDFS的源代码进行解析,以便更好地理解其工作原理。

HDFS的核心组件

HDFS由三个核心组件组成:NameNode,DataNode和客户端。NameNode是HDFS的主节点,负责管理文件系统的命名空间和客户端的元数据。DataNode是HDFS的从节点,负责存储实际的数据块。客户端是与HDFS交互的用户程序,它们可以读取或写入数据。

  1. NameNode启动流程
  • 1.1 NameNode
  • 1.2 启动流程main方法
    • startHttpServer 启动HTTPServer

    • loadNamesystem 加载元数据 todo

      • 从磁盘上加载FSFSImage到内存中合并元数据
      • JournalSet的成员变量journals List根据配置读取到NameNode的路径本地和JournalNode路径远程,生成FileJournalnodeManager和QuorumJournalManager
      • Fsimage合并后写入到磁盘上
      • 打开editLog开始写日志,初始化两个流用于写入日志使用 QuorumJournalManager-》QuorumOutputStream FileJournalManager -》 EditLogFileOutputStream
    • createRpcServer

      • 创建ServiceRPCServer
      • 创建ClientRpcServer
    • startCommonServices 公共服务启动

      • 检查是否有足够的磁盘存储元数据
        • 通过扫描core-site.xml hdfs-site.xml知道哪些磁盘用于存储元数据
        • 遍历磁盘如果磁盘空间小于100M就返回false
      • 安全模式检查
        • 如果datanode累计汇报的block的数目小于0.999(默认)*总的block个数,进入安全模式
        • 如果存活的datanode小于某个阈值的时候也会进入安全模式且阈值不等于零,进入安全模式,默认阈值为零
        • 存放元数据的空间是否大于100M如果不大于100M也会进入安全模式
      • 启动管理心跳的服务 HeartbeatManager的一个监听心跳的一个成员变量线程,并启动这个线程,跳转到3
    • 如果是NameNode(Secondry)还会启动EditLogTailer线程从Journalnode上同步元数据,启动StandbyCheckpointer用于检测NameNode的EditLog和FsImage的数据是否需要合并

  1. DateNode启动流程
  • 2.1 DateNode

  • 2.2 启动流程main方法

    • createDataNode
    • initDataXceiver
      • 实例化 DataXceiverServer 线程用于DataNode用来接收客户端和其他DataNode传过来数据的服务
    • startInfoServer 启动http服务
    • initIpcServer 启动RPC服务
    • 创建了BlockPoolManager
      • 一个联邦只有一个BlackPool,对应一个BPOfferService
      • 一个联邦一般有两个NameNode(avtive和standBy)会启动两个BPServiceActor线程用于DataNode向NameNode(一个是NameNode active 一个是NameNode standBy)进行注册
  • 2.3 向NameNode进行注册

    • 首先获取到NameNode的代理,校验NameSpace、clusterId、BlockPoolId的信息。
    • 创建注册信息,通过NameNode的NameNodeRpcServer的registerDatanode向NameNode进行注册
    • 通过NameNodeRpcServer的FSNamesystem成员变量获取DataNodeManager往DataNodeManager的各种内存结构里面添加各种信息
    • 把注册上来的DataNode加入到HeartbeatManager中用于心跳管理
  • 2.4 向NameNode发送心跳

    • 每隔3秒钟通过NameNodeRpcServer服务向NameNode进行注册,从DataNodeManager中获取注册DataNode的注册信息,主要更改心跳时间,并回调带回来一些NameNode的发送过来的指令。
  • 2.5 启动blockPoolManager、dataXceiverServer、ipcServer守护线程

  1. NameNode和DataNode心跳管理
    • NameNode启动公共服务 startCommonServices的时候会启动一个HeartbeatMananger的的线程服务用来检查DataNode的心跳信息,判断是否死去,查看HeartbeatManger的run方法。
    • 每隔30s或者五分钟执行一次心态检查,如果10分三十秒还没有检测到心跳,那么就将这个DataNode设置为死亡状态,从DatanodeManager里面将各种DataNode各种注册信息移除掉。
  2. 元数据管理流程
    • 场景驱动FileTest类创建目录
    • DistributedFileSystem mkdirs 调用RpcNameNodeServer mkdirs
    • 首先更新NameNode 的内存中的目录树结构
    • 创建日志对象,并将日志对象记录
      • 将数据先写入到内存的缓冲区中,交换内存的缓冲数据,将数据写入到NameNode的本地磁盘中。
      • 将数据先写入到内存的缓冲区中,交换内存的缓冲数据,同时将数据发送到journalNode
      • 异步获取到每一个journalNode的JournalNodeRpcServerd的journal方法,通过rpc服务将数据写入到journalNode的本地磁盘中
    • SeconderyNameNode的EditLogTailer线程会同步Journal的元数据到StandByNameNode上面去
      • 当SeconderyNameNode启动后就会启动EditLogTailer线程从JournalNode来同步元数据到StandByNameNode上
      • 首先加载当前自己的元数据日志,并获取当前的元数据日志的最后一条日志的事务ID,通过Http请求去JournalNode上读取日志,并将获取到的元数据作用到自己的内存的元数据目录树里面里面
    • SeconderyNameNode的StandByCheckPointer线程的checkpoint (EditLogTailerh和StandByCheckPointer都是NameNode启动的时候会启动),StandByNameNode会往NameNodeHttpServer的imagetransfer(ImageServlet的put方法)发送元数据
      • SeconderyNameNode启动的时候会启动一个StandByCheckPointer线程每隔60秒检验是否需要Checkpoint
      • 先获取到当前的最新的日志的事务Id,然后获取到上一次CheckPoint的事务Id,两者相减如果大于等于100万条就需要做checkpoint
      • 获取到当前时间和上一次CheckPonit的时间,两者相减如果>=1个小时没有做CheckPoint,那么就需要做一次checkpoint
      • 如果满足CheckPoint的其中一个条件,就开始做CheckPoint.
        • 开启一个线程将SeconderyNameNode的元数据持久化到磁盘上面
        • 开启一个异步的线程将SeconderyNameNode,通过Http请求的put方式将数据发送到activeNameNode的imagetransfer这个路由上面
        • activeNameNode的ImageServlet的doPut方法不断获取到输入流,保存文件到本地,并将fsimage_N.ckpt重命名为fsimage_N这个文件
  3. HDFS写数据流程
    • 5.1 场景驱动FileTest类写数据 的create()
    • DFSOutputStream.newStreamForCreate首先客户端获取到NameNodeRpcServer的create的方法
      • 调用RpcNameNodeServer服务往NameNode的文件目录树添加INodeFile,并将元数据写入本地磁盘和JournalNode中
      • NameNode leaseManger.addLease()添加契约 将数据放入到一个可排序(按照时间倒叙)的数据结构中,并添加契约的时间。
      • NameNode启动的时候会启动一个LeaseManager的监听线程,每隔两秒钟就会从可排序的数据结构中,拿出第一个契约,如果第一个契约没有过期就直接return,否则再看后续的契约是否过期。如果有过期的数据就从各种数据结构中移除数据。
    • 客户端DFSOutputStream创建并启动DataStreamer线程是写数据的重要对象
      • 当开始启动DataStreamer线程的时候,由于dataQueue里面没有数据会阻塞住线程,只有客户端调用write方法真正开始写数据的时候,该线程才会继续运行
      • 当队列中有了数据DataStreamer就会被唤醒并运行
        • 首先向NameNode申请Block
          • 调用NameNodeRpcServer选择存放Block的DataNode目标主机数组
          • 修改内存里面的目录树信息,将block信息记录到内存的目录树中
          • 并将元数据写入到磁盘中
        • 客户端通过Socket请求连接到第一个DataNode的目标主机,创建输出流
        • 启动一个ResponseProcessor线程来监听发送的状态,读取下游的结果,如果发送成功就把ackQueue里面的packet移除。
        • 从dataQueue把要发送的这个packet移除出去,然后往ackQueue里面添加这个packet
        • 通过输出流把数据写出去
        • 写数据异常
          • 把ackQueue的数据写入到dataQueue,清空ackQueue队列
          • 重新建立数据管道
            • 如果有一半以上的都有问题,重新构建新的数据管道
            • 如果只有一个出问题,就将剩余的重新构建数据管道,然后同步数据即可,后面NameNode会检测到DataNode少了副本就会通知DataNode处理
            • 当管道建立成功之后就重新开始写数据
    • beginFileLease()
      • 客户端开启一个线程每隔一秒钟检查一次,如果超过三十秒没有续约,那么就调用NameNodeRpcServer续约契约的更新时间,移除老的数据结构,更新契约时间,将新的数据放入到数据结构里面。
      • LeaseManager有个线程移除老的契约时间。
    • 5.2 FileTest 写数据 的write()方法
      • DFSOutputStream父类的write方法写入数据,最终调用DFSOutputStream的父方法FSOutputSummer的write方法
      • HDFS文件 -》 Block文件块(128M) -》 2048 * packet(64K) = 127 * chunk -> chunk 512byte + chunksum 4byte = 516
      • 开始以一个chunk的开始写,当写满一个packet或者写满一个Block,就会往dataQueue里面添加packte,然后唤醒DataStreamer的run方法,
        • 如果没有写满的话,等到DataStreamer过了超时时间也会从dataQueue取数据,没有写满一个packet,会利用已有的数据创建一个packet来发送数据
      • 当datanode获取到DataStreamer发送过来的数据后
        • 每发送过来一个请求DataXceiverServer线程都会启动DataXceiver线程用于处理数据。
        • 根据不同的请求的操作类型,我们这边是写数据,那么会启动BlockReceive线程,通过socket继续连接下游的datanode,建立通道
        • 启动PacketResponder和 ResponseProcessor线程功能类似,是用于判断下游是否写数据成功,成功移除ackQueue
          • 获取当前节点是否已经写入成功
          • 获取下游节点的处理结果
          • 往上游发送处理结果
          • 获取到下游的处理结果,如果处理成功,就从当前的datanode节点的ackQueue队列中移除packet.
        • 不断的接受数据
          • 把packet写入到datanode的ackqueue队列中
          • 通过上面连接的数据通道把当前的packet发送的下游的datanode节点
          • 校验数据并将数据写入到本地。
        • 往上游写回相应结果

NameNode源代码总结

NameNode是HDFS的核心组件之一,它负责管理文件系统的命名空间和客户端的元数据。以下是NameNode源代码的主要组成部分:

  1. FsNamesystem:这是NameNode的核心组件之一,它负责管理文件系统的命名空间和客户端的元数据。它包含了文件系统的目录树和文件元数据,如文件大小、创建时间、修改时间等。

  2. FSImage:这是文件系统镜像的核心组件,它是文件系统的元数据的持久化存储。它将文件系统的元数据保存在本地磁盘上,以便在NameNode重新启动时恢复文件系统的状态。

  3. EditLog:这是文件系统编辑日志的核心组件,它记录了文件系统的所有修改操作。它将修改操作写入本地磁盘,以便在NameNode重新启动时重新应用这些操作。

  4. NameNodeRpcServer:这是NameNode的RPC服务器,它处理客户端和DataNode之间的通信。它提供了一组RPC接口,用于管理文件系统的命名空间和元数据。

DataNod总结

DataNode是HDFS的从节点,它负责存储实际的数据块。以下是DataNode源代码的主要组成部分:

  1. FsDatasetImpl:这是DataNode的核心组件之一,它负责存储实际的数据块。它包含了文件系统的数据块和元数据,如块大小、创建时间、修改时间等。

  2. DataNodeRpcServer:这是DataNode的RPC服务器,它处理客户端和NameNode之间的通信。它提供了一组RPC接口,用于管理数据块和元数据。

  3. BlockReceiver:这是DataNode接收数据块的核心组件。它负责接收数据块并将其写入本地磁盘。

客户端源代码总结

客户端是与HDFS交互的用户程序,它们可以读取或写入数据。以下是客户端源代码的主要组成部分:

  1. DistributedFileSystem:这是客户端的核心组件之一,它提供了一组API,用于读取或写入数据。它将API调用转换为RPC请求,并将请求发送到NameNode或DataNode。

  2. DFSClient:这是客户端的核心组件之一,它负责与NameNode和DataNode之间的通信。它将API调用转换为RPC请求,并将请求发送到NameNode或DataNode。

总结

本文对HDFS的源代码进行了简要的解析,以便更好地理解其工作原理。HDFS的核心组件包括NameNode、DataNode和客户端,它们分别负责管理文件系统的命名空间和元数据、存储实际的数据块以及与HDFS交互。HDFS的源代码是Hadoop生态系统的核心组件之一,它为存储大量数据提供了可扩展的分布式文件系统。

你可能感兴趣的:(hdfs,hadoop,大数据)