关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »HDFS读文件过程分析:获取文件对应的Block列表

    HDFS读文件过程分析:获取文件对应的Block列表

    2014-11-01 00:00:00 出处:ASCE1885
    分享

    在使用Java读取一个文件系统中的一个文件时,我们会首先构造一个DataInputStream对象,然后就能够从文件中读取数据。对于存储在HDFS上的文件,也对应着类似的工具类,但是底层的实现逻辑却是非常不同的。我们先从使用DFSClient.DFSDataInputStream类来读取HDFS上一个文件的一段代码来看,如下所示:

    package org.shirdrn.hadoop.hdfs;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.FSDataInputStream;
    import org.apache.hadoop.fs.FileSystem;
    import org.apache.hadoop.fs.Path;
    
    public class HdfsFileReader {
    
         public static void main(String[] args) {
              String file = "hdfs://hadoop-cluster-m:8020/data/logs/basis_user_behavior/201405071237_10_10_1_73.log";
              Path path = new Path(file);
    
              Configuration conf = new Configuration();
              FileSystem fs;
              FSDataInputStream in;
              BufferedReader reader = null;
              try {
                   fs = FileSystem.get(conf);
                   in = fs.open(path); // 打开文件path,返回一个FSDataInputStream流对象
                   reader = new BufferedReader(new InputStreamReader(in));
                   String line = null;
                   while((line = reader.readLine()) != null) { // 读取文件行内容
                        System.out.println("Record: " + line);
                   }
              } catch (IOException e) {
                   e.printStackTrace();
              } finally {
                   try {
                        if(reader != null) reader.close();
                   } catch (IOException e) {
                        e.printStackTrace();
                   }
              }
         }
    
    }

    基于上面代码,我们可以看到,通过一个FileSystem对象可以打开一个Path文件,返回一个FSDataInputStream文件输入流对象,然后从该FSDataInputStream对象就能够读取出文件的内容。所以,我们从FSDataInputStream入手,详细分析从HDFS读取文件内容的过程,在实际地读取物理数据块之前,首先要获取到文件对应的Block列表元数据信息,整体流程如下图所示:

    hdfs-get-block-locations

    下面,详细说明整个流程:

    创建FSDataInputStream流对象

    从一个Path路径对象,能够获取到一个FileSystem对象,然后通过调用FileSystem的open方法打开一个文件流:

    public FSDataInputStream open(Path f) throws IOException {
      return open(f, getConf().getInt("io.file.buffer.size", 4096));
    }

    由于FileSystem是抽象类,将具体的打开操作留给具体子类实现,例如FTPFileSystem、HarFileSystem、WebHdfsFileSystem等,不同的文件系统具有不同打开文件的行为,我们以DistributedFileSystem为例,open方法实现,代码如下所示:

    public FSDataInputStream open(Path f, int bufferSize) throws IOException {
      statistics.incrementReadOps(1);
      return new DFSClient.DFSDataInputStream(
            dfs.open(getPathName(f), bufferSize, verifyChecksum, statistics));
    }

    statistics对象用来收集文件系统操作的统计数据,这里使读取文件操作的计数器加1。然后创建了一个DFSClient.DFSDataInputStream对象,该对象的参数是通过DFSClient dfs客户端对象打开一个这个文件从而返回一个DFSInputStream对象,下面,我们看DFSClient的open方法实现,代码如下所示:

    public DFSInputStream open(String src, int buffersize, boolean verifyChecksum,
                        FileSystem.Statistics stats) throws IOException {
      checkOpen();
      //    Get block info from namenode
      return new DFSInputStream(src, buffersize, verifyChecksum);
    }

    checkOpen方法就是检查一个标志位clientRunning,表示当前的dfs客户端对象是否已经创建并初始化,在dfs客户端创建的时候该标志就为true,表示客户端正在运行状态。我们知道,当客户端DFSClient连接到Namenode的时候,实际上是创建了一个到Namenode的RPC连接,Namenode作为Server角色,DFSClient作为Client角色,它们之间建立起Socket连接。只有显式调用DFSClient的close方法时,才会修改clientRunning的值为false,实际上真正地关闭了已经建立的RPC连接。

    我们看一下创建DFSInputStream的构造方法实现:

    DFSInputStream(String src, int buffersize, boolean verifyChecksum) throws IOException {
      this.verifyChecksum = verifyChecksum;
      this.buffersize = buffersize;
      this.src = src;
      prefetchSize = conf.getLong("dfs.read.prefetch.size", prefetchSize);
      openInfo();
    }

    先设置了几个与读取文件相关的参数值,这里有一个预先读取文件的Block字节数的参数prefetchSize,它的值设置如下:

    public static final long DEFAULT_BLOCK_SIZE = DFSConfigKeys.DFS_BLOCK_SIZE_DEFAULT;
    public static final long    DFS_BLOCK_SIZE_DEFAULT = 64*1024*1024;
    
      defaultBlockSize = conf.getLong("dfs.block.size", DEFAULT_BLOCK_SIZE);
      private long prefetchSize = 10 * defaultBlockSize;

    这个prefetchSize的值默认为10*64*1024*1024=671088640,也就是说,默认预读取一个文件的10个块,即671088640B=640M,如果想要修改这个值,设置dfs.block.size即可覆盖默认值。

    然后调用了openInfo方法,从Namenode获取到该打开文件的信息,在openInfo方法中,具体实现如下所示:

    synchronized void openInfo() throws IOException {
      for (int retries = 3; retries > 0; retries--) {
        if (fetchLocatedBlocks()) { // fetch block success. 如果成功获取到待读取文件对应的Block列表,则直接返回
          return;
        } else {
          // Last block location unavailable. When a cluster restarts,
          // DNs may not report immediately. At this time partial block
          // locations will not be available with NN for getting the length.
          // Lets retry a few times to get the length.
          DFSClient.LOG.warn("Last block locations unavailable. "
              + "Datanodes might not have reported blocks completely."
              + " Will retry for " + retries + " times");
          waitFor(4000);
        }
      }
      throw new IOException("Could not obtain the last block locations.");
    }

    上述代码中,有一个for循环用来获取Block列表。如果成功获取到待读取文件的Block列表,则直接返回,否则,最多执行3次等待重试操作(最多花费时间大于12秒)。未能成功读取文件的Block列表信息,是因为Namenode无法获取到文件对应的块列表的信息,当整个集群启动的时候,Datanode会主动向NNamenode上报对应的Block信息,只有Block Report完成之后,Namenode就能够知道组成文件的Block及其所在Datanode列表的信息。openInfo方法方法中调用了fetchLocatedBlocks方法,用来与Namenode进行RPC通信调用,实际获取对应的Block列表,实现代码如下所示:

    private boolean fetchLocatedBlocks() throws IOException,
        FileNotFoundException {
      LocatedBlocks newInfo = callGetBlockLocations(namenode, src, 0, prefetchSize);
      if (newInfo == null) {
        throw new FileNotFoundException("File does not exist: " + src);
      }
    
      if (locatedBlocks != null && !locatedBlocks.isUnderConstruction() && !newInfo.isUnderConstruction()) {
        Iterator<LocatedBlock> oldIter = locatedBlocks.getLocatedBlocks().iterator();
        Iterator<LocatedBlock> newIter = newInfo.getLocatedBlocks().iterator();
        while (oldIter.hasNext() && newIter.hasNext()) {
          if (!oldIter.next().getBlock().equals(newIter.next().getBlock())) {
            throw new IOException("Blocklist for " + src + " has changed!");
          }
        }
      }
      boolean isBlkInfoUpdated = updateBlockInfo(newInfo);
      this.locatedBlocks = newInfo;
      this.currentNode = null;
      return isBlkInfoUpdated;
    }

    调用callGetBlockLocations方法,实际上是根据创建RPC连接以后得到的Namenode的代理对象,调用Namenode来获取到指定文件的Block的位置信息(位于哪些Datanode节点上):namenode.getBlockLocations(src, start, length)。调用callGetBlockLocations方法返回一个LocatedBlocks对象,该对象包含了文件长度信息、List blocks列表对象,其中LocatedBlock包含了一个Block的基本信息:

    private Block b;
    private long offset;  // offset of the first byte of the block in the file
    private DatanodeInfo[] locs;
    private boolean corrupt;

    有了这些文件的信息(文件长度、文件包含的Block的位置等信息),DFSClient就能够执行后续读取文件数据的操作了,详细过程我们在后面分析说明。

    通过Namenode获取文件信息

    上面,我们提到获取一个文件的基本信息,是通过Namenode来得到的,这里详细分析Namenode是如何获取到这些文件信息的,实现方法getBlockLocations的代码,如下所示:

    public LocatedBlocks getBlockLocations(String src, long offset, long length) throws IOException {
      myMetrics.incrNumGetBlockLocations();
      return namesystem.getBlockLocations(getClientMachine(), src, offset, length);
    }

    可以看到,Namenode又委托管理HDFS name元数据的FSNamesystem的getBlockLocations方法实现:

    LocatedBlocks getBlockLocations(String clientMachine, String src, long offset, long length) throws IOException {
      LocatedBlocks blocks = getBlockLocations(src, offset, length, true, true, true);
      if (blocks != null) {
        //sort the blocks
        // In some deployment cases, cluster is with separation of task tracker
        // and datanode which means client machines will not always be recognized
        // as known data nodes, so here we should try to get node (but not
        // datanode only) for locality based sort.
        Node client = host2DataNodeMap.getDatanodeByHost(clientMachine);
        if (client == null) {
          List<String> hosts = new ArrayList<String> (1);
          hosts.add(clientMachine);
          String rName = dnsToSwitchMapping.resolve(hosts).get(0);
          if (rName != null)
            client = new NodeBase(clientMachine, rName);
        }  
    
        DFSUtil.StaleComparator comparator = null;
        if (avoidStaleDataNodesForRead) {
          comparator = new DFSUtil.StaleComparator(staleInterval);
        }
        // Note: the last block is also included and sorted
        for (LocatedBlock b : blocks.getLocatedBlocks()) {
          clusterMap.pseudoSortByDistance(client, b.getLocations());
          if (avoidStaleDataNodesForRead) {
            Arrays.sort(b.getLocations(), comparator);
          }
        }
      }
      return blocks;
    }

    跟踪代码,最终会在下面的方法中实现了,如何获取到待读取文件的Block的元数据列表,以及如何取出该文件的各个Block的数据,方法实现代码,这里我做了详细的注释,可以参考,如下所示:

    private synchronized LocatedBlocks getBlockLocationsInternal(String src,
                                                         long offset,
                                                         long length,
                                                         int nrBlocksToReturn,
                                                         boolean doAccessTime,
                                                         boolean needBlockToken)
                                                         throws IOException {
            INodeFile inode = dir.getFileINode(src);  // 获取到与待读取文件相关的inode数据
            if (inode == null) {
                 return null;
            }
            if (doAccessTime && isAccessTimeSupported()) {
                 dir.setTimes(src, inode, -1, now(), false);
            }
            Block[] blocks = inode.getBlocks(); // 获取到文件src所包含的Block的元数据列表信息
            if (blocks == null) {
                 return null;
            }
            if (blocks.length == 0) { // 获取到文件src的Block数,这里=0,该文件的Block数据还没创建,可能正在创建
                 return inode.createLocatedBlocks(new ArrayList<LocatedBlock>(blocks.length));
            }
            List<LocatedBlock> results;
            results = new ArrayList<LocatedBlock>(blocks.length);
    
            int curBlk = 0; // 当前Block在Block[] blocks数组中的索引位置
            long curPos = 0, blkSize = 0; // curPos表示某个block在文件中的字节偏移量,blkSize为Block的大小(字节数)
            int nrBlocks = (blocks[0].getNumBytes() == 0)   0 : blocks.length; // 获取到文件src的Block数,实际上一定>0,但是第一个block大小可能为0,这种情况认为nrBlocks=0
            for (curBlk = 0; curBlk < nrBlocks; curBlk++) {  // 根据前面代码,我们知道offset=0,所以这个循环第一次进来肯定就break出去了(正常的话,blkSize>0,所以我觉得这段代码写的稍微有点晦涩)
                 blkSize = blocks[curBlk].getNumBytes();
                 assert blkSize > 0 : "Block of size 0";
                 if (curPos + blkSize > offset) {
                      break;
                 }
                 curPos += blkSize;
            }
    
            if (nrBlocks > 0 && curBlk == nrBlocks) // offset >= end of file, 到这里curBlk=0,如果从文件src的第一个Block的字节数累加计算,知道所有的Block的字节数都累加上了,总字节数仍然<=请求的offset,说明即使到了文件尾部,仍然没有达到offset的值。从前面fetchLocatedBlocks()方法中调用我们知道,offset=0,所以执行该分支表示文件src没有可用的Block数据块可读
                 return null;
    
            long endOff = offset + length; // 
    
            do {
                 // 获取Block所在位置(Datanode节点)
                 int numNodes = blocksMap.numNodes(blocks[curBlk]); // 计算文件src中第curBlk个Block存储在哪些Datanode节点上
                 int numCorruptNodes = countNodes(blocks[curBlk]).corruptReplicas(); // 计算存储文件src中第curBlk个Block但无法读取该Block的Datanode节点数
                 int numCorruptReplicas = corruptReplicas.numCorruptReplicas(blocks[curBlk]); // 计算FSNamesystem在内存中维护的Block=>Datanode映射的列表中,无法读取该Block的Datanode节点数
                 if (numCorruptNodes != numCorruptReplicas) {
                      LOG.warn("Inconsistent number of corrupt replicas for "
                                + blocks[curBlk] + "blockMap has " + numCorruptNodes
                                + " but corrupt replicas map has " + numCorruptReplicas);
                 }
                 DatanodeDescriptor[] machineSet = null;  // 下面的if...else用来获取一个Block所在的Datanode节点
                 boolean blockCorrupt = false;
                 if (inode.isUnderConstruction() && curBlk == blocks.length - 1
                           && blocksMap.numNodes(blocks[curBlk]) == 0) { // 如果文件正在创建,当前blocks[curBlk]还没有创建成功(即没有可用的Datanode可以提供该Block的服务),仍然返回待创建Block所在的Datanode节点列表。数据块是在Datanode上存储的,只要Datanode完成数据块的存储后,通过heartbeat将数据块的信息上报给Namenode后,这些信息才会存储到blocksMap中
                      // get unfinished block locations
                      INodeFileUnderConstruction cons = (INodeFileUnderConstruction) inode;
                      machineSet = cons.getTargets();
                      blockCorrupt = false;
                 } else { // 文件已经创建完成
                      blockCorrupt = (numCorruptNodes == numNodes); // 是否当前的Block在所有Datanode节点上的副本都坏掉,无法提供服务
                      int numMachineSet = blockCorrupt   numNodes : (numNodes - numCorruptNodes); // 如果是,则返回所有Datanode节点,否则,只返回可用的Block副本所在的Datanode节点
                      machineSet = new DatanodeDescriptor[numMachineSet];
                      if (numMachineSet > 0) { // 获取到当前Block所有副本所在的Datanode节点列表
                           numNodes = 0;
                           for (Iterator<DatanodeDescriptor> it = blocksMap.nodeIterator(blocks[curBlk]); it.hasNext();) {
                                DatanodeDescriptor dn = it.next();
                                boolean replicaCorrupt = corruptReplicas.isReplicaCorrupt(blocks[curBlk], dn);
                                if (blockCorrupt || (!blockCorrupt && !replicaCorrupt))
                                     machineSet[numNodes++] = dn;
                           }
                      }
                 }
                 LocatedBlock b = new LocatedBlock(blocks[curBlk], machineSet, curPos, blockCorrupt); // 创建一个包含Block的元数据对象、所在Datanode节点列表、起始索引位置(字节数)、健康状况的LocatedBlock对象
                 if (isAccessTokenEnabled && needBlockToken) { // 如果启用Block级的令牌(Token)访问,则为当前用户生成读模式的令牌信息,一同封装到返回的LocatedBlock对象中
                      b.setBlockToken(accessTokenHandler.generateToken(b.getBlock(), EnumSet.of(BlockTokenSecretManager.AccessMode.READ)));
                 }
    
                 results.add(b); // 收集待返回给读取文件的客户端需要的LocatedBlock列表
                 curPos += blocks[curBlk].getNumBytes();
                 curBlk++;
            } while (curPos < endOff && curBlk < blocks.length && results.size() < nrBlocksToReturn);
    
            return inode.createLocatedBlocks(results); // 将收集的LocatedBlock列表数据封装到一个LocatedBlocks对象中返回
       }

    我们可以看一下,最后的调用inode.createLocatedBlocks(results)生成LocatedBlocks对象的实现,代码如下所示:

    LocatedBlocks createLocatedBlocks(List<LocatedBlock> blocks) {
      return new LocatedBlocks(computeContentSummary().getLength(), blocks, isUnderConstruction()); // 通过ContentSummary对象获取到文件的长度
    }

    客户端通过RPC调用,获取到了文件对应的Block以及所在Datanode列表的信息,然后就可以根据LocatedBlocks来进一步获取到对应的Block对应的物理数据块。

    对Block列表进行排序

    我们再回到FSNamesystem类,调用getBlockLocationsInternal方法的getBlockLocations方法中,在返回文件block列表LocatedBlocks之后,会对每一个Block所在的Datanode进行的一个排序,排序的基本规则有如下2点:

    Client到Block所在的Datanode的距离最近,这个是通过网络拓扑关系来进行计算,例如Client的网络路径为/dc1/r1/c1,那么路径为/dc1/r1/dn1的Datanode就比路径为/dc1/r2/dn2的距离小,/dc1/r1/dn1对应的Block就会排在前面 从上面一点可以推出,如果Client就是某个Datanode,恰好某个Block的Datanode列表中包括该Datanode,则该Datanode对应的Block排在前面 Block所在的Datanode列表中,如果其中某个Datanode在指定的时间内没有向Namenode发送heartbeat(默认由常量DFSConfigKeys.DFS_NAMENODE_STALE_DATANODE_INTERVAL_DEFAULT定义,默认值为30s),则该Datanode的状态即为STALE,具有该状态的Datanode对应的Block排在后面

    基于上述规则排序后,Block列表返回到Client。

    Client与Datanode交互更新文件Block列表

    我们要回到前面分析的DFSClient.DFSInputStream.fetchLocatedBlocks()方法中,查看在调用该方法之后,是如何执行实际处理逻辑的:

    private boolean fetchLocatedBlocks() throws IOException,
        FileNotFoundException {
      LocatedBlocks newInfo = callGetBlockLocations(namenode, src, 0, prefetchSize); // RPC调用向Namenode获取待读取文件对应的Block及其位置信息LocatedBlocks对象
      if (newInfo == null) {
        throw new FileNotFoundException("File does not exist: " + src);
      }
    
      if (locatedBlocks != null && !locatedBlocks.isUnderConstruction() && !newInfo.isUnderConstruction()) { // 这里面locatedBlocks!=null是和后面调用updateBlockInfo方法返回的状态有关的
        Iterator<LocatedBlock> oldIter = locatedBlocks.getLocatedBlocks().iterator();
        Iterator<LocatedBlock> newIter = newInfo.getLocatedBlocks().iterator();
        while (oldIter.hasNext() && newIter.hasNext()) { // 检查2次获取到的LocatedBlock列表:第2次得到newInfo包含的Block列表,在第2次得到的locatedBlocks中是否发生变化,如果发生了变化,则不允许读取,抛出异常
          if (!oldIter.next().getBlock().equals(newIter.next().getBlock())) {
            throw new IOException("Blocklist for " + src + " has changed!");
          }
        }
      }
      boolean isBlkInfoUpdated = updateBlockInfo(newInfo);
      this.locatedBlocks = newInfo;
      this.currentNode = null;
      return isBlkInfoUpdated;
    }

    如果第一次读取该文件时,已经获取到了对应的block列表,缓存在客户端;如果客户端第二次又读取了该文件,仍然获取到一个block列表对象。在两次读取之间,可能存在原文件完全被重写的情况,所以新得到的block列表与原列表完全不同了,存在这种情况,客户端直接抛出IO异常,如果原文件对应的block列表没有变化,则更新客户端缓存的对应block列表信息。

    当集群重启的时候(如果允许安全模式下读文件),或者当一个文件正在创建的时候,Datanode向Namenode进行Block Report,这个过程中可能Namenode还没有完全重建好Block到Datanode的映射关系信息,所以即使在这种情况下,仍然会返回对应的正在创建的Block所在的Datanode列表信息,可以从前面getBlockLocationsInternal方法中看到,INode的对应UnderConstruction状态为true。这时,一个Block对应的所有副本中的某些可能还在创建过程中。

    上面方法中,调用updateBlockInfo来更新文件的Block元数据列表信息,对于文件的某些Block可能没有创建完成,所以Namenode所保存的关于文件的Block的的元数据信息可能没有及时更新(Datanode可能还没有完成Block的报告),代码实现如下所示:

    private boolean updateBlockInfo(LocatedBlocks newInfo) throws IOException {
      if (!serverSupportsHdfs200 || !newInfo.isUnderConstruction() || !(newInfo.locatedBlockCount() > 0)) { // 如果获取到的newInfo可以读取文件对应的Block信息,则返回true
        return true;
      }
    
      LocatedBlock last = newInfo.get(newInfo.locatedBlockCount() - 1); // 从Namenode获取文件的最后一个Block的元数据对象LocatedBlock
      boolean lastBlockInFile = (last.getStartOffset() + last.getBlockSize() == newInfo.getFileLength()); 
      if (!lastBlockInFile) { // 如果“文件长度 != 最后一个块起始偏移量 + 最后一个块长度”,说明文件对应Block的元数据信息还没有更新,但是仍然返回给读取文件的该客户端
        return true;
      }
      // 这时,已经确定last是该文件的最后一个bolck,检查最后个block的存储位置信息
      if (last.getLocations().length == 0) {
        return false;
      }
    
      ClientDatanodeProtocol primary = null;
      Block newBlock = null;
      for (int i = 0; i < last.getLocations().length && newBlock == null; i++) { // 根据从Namenode获取到的LocatedBlock last中对应的Datanode列表信息,Client与Datanode建立RPC连接,获取最后一个Block的元数据
        DatanodeInfo datanode = last.getLocations()[i];
        try {
          primary = createClientDatanodeProtocolProxy(datanode, conf, last .getBlock(), last.getBlockToken(), socketTimeout, connectToDnViaHostname);
          newBlock = primary.getBlockInfo(last.getBlock());
        } catch (IOException e) {
          if (e.getMessage().startsWith(
              "java.io.IOException: java.lang.NoSuchMethodException: "
                  + "org.apache.hadoop.hdfs.protocol"
                  + ".ClientDatanodeProtocol.getBlockInfo")) {
            // We're talking to a server that doesn't implement HDFS-200.
            serverSupportsHdfs200 = false;
          } else {
            LOG.info("Failed to get block info from "
                + datanode.getHostName() + " probably does not have "
                + last.getBlock(), e);
          }
        } finally {
          if (primary != null) {
            RPC.stopProxy(primary);
          }
        }
      }
    
      if (newBlock == null) { // Datanode上不存在最后一个Block对应的元数据信息,直接返回
        if (!serverSupportsHdfs200) {
          return true;
        }
        throw new IOException("Failed to get block info from any of the DN in pipeline: " + Arrays.toString(last.getLocations()));
      }
    
      long newBlockSize = newBlock.getNumBytes();
      long delta = newBlockSize - last.getBlockSize();
      // 对于文件的最后一个Block,如果从Namenode获取到的元数据,与从Datanode实际获取到的元数据不同,则以Datanode获取的为准,因为可能Datanode还没有及时将Block的变化信息向Namenode汇报
      last.getBlock().setNumBytes(newBlockSize);
      long newlength = newInfo.getFileLength() + delta;
      newInfo.setFileLength(newlength); // 修改文件Block和位置元数据列表信息
      LOG.debug("DFSClient setting last block " + last + " to length " + newBlockSize + " filesize is now " + newInfo.getFileLength());
      return true;
    }

    我们看一下,在updateBlockInfo方法中,返回false的情况:Client向Namenode发起的RPC请求,已经获取到了组成该文件的数据块的元数据信息列表,但是,文件的最后一个数据块的存储位置信息无法获取到,说明Datanode还没有及时通过block report将数据块的存储位置信息报告给Namenode。通过在openInfo()方法中可以看到,获取文件的block列表信息有3次重试机会,也就是调用updateBlockInfo方法返回false,可以有12秒的时间,等待Datanode向Namenode汇报文件的最后一个块的位置信息,以及Namenode更新内存中保存的文件对应的数据块列表元数据信息。

    我们再看一下,在updateBlockInfo方法中,返回true的情况:

    文件已经创建完成,文件对应的block列表元数据信息可用 文件正在创建中,但是当前能够读取到的已经完成的最后一个块(非组成文件的最后一个block)的元数据信息可用 文件正在创建中,文件的最后一个block的元数据部分可读:从Namenode无法获取到该block对应的位置信息,这时Client会与Datanode直接进行RPC通信,获取到该文件最后一个block的位置信息

    上面Client会与Datanode直接进行RPC通信,获取文件最后一个block的元数据,这时可能由于网络问题等等,无法得到文件最后一个block的元数据,所以也会返回true,也就是说,Client仍然可以读取该文件,只是无法读取到最后一个block的数据。

    这样,在Client从Namenode/Datanode获取到的文件的Block列表元数据已经是可用的信息,可以根据这些信息读取到各个Block的物理数据块内容了,准确地说,应该是文件处于打开状态了,已经准备好后续进行的读操作了。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    最全面的前端开发指南

    Windows7任务栏桌面下角的一些正在运行的图标不见了

    sql server快速删除记录方法

    SQL Server 7移动数据的6种方法

    SQL Server 2008的新压缩特性

    每个Java程序员必须知道的5个JVM命令行标志

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。