HDFS Client Read流程分析

python使用hdfs库操作Hadoop的HDFS 此次使用python的hdfs库操作HDFS,首相安装该库:pip install hdfs 其次,要保证HDFS可用,如下图就代表可用,当然你列出的文件和我的不同 老规矩,先来看看它这个库的大概结构,方便以后调用。先 import hdfs ,然后跟进hdfs库,查看定义它的地方。如下,既然是连接的库,那么这个client就是连接的客户端了,这里出现了三个client,它们有什么不同 首先是Client类,转到定义处之后,可以发现它里面有很多操作,基本的增删查改都有了,看它的 __init__ 方法, 阅读详情

概述

参考:https://www.jianshu.com/p/3f93991b31da

hdfs读流程总体分为:

  1. 获取文件流(包括块发送RPC获得块信息等)
    FSDataInputStream fsIn = FileSystem.open(“path”)
  2. 读文件
    fsIn.read(buf, off, toRead)

流程图如下:
在这里插入图片描述
说明:

  • 上述step3,目的是获得块信息,是通过调用dfsClient来完成的

1. 建入口类断点调试

加入hdfs依赖:
pom.xml

    ...
    <properties>
    	<hadoop.version>2.7.2-2323</hadoop.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
    </dependencies>
    ...        

我们断点调试hdfs读数据过程,为了方便调试,写一个测试类:

public class FsShellTest {
    public static void main(String argv[]) throws Exception {

        FsShell shell = new FsShell();
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS","hdfs://hadoop1:9000");
        conf.setQuietMode(false);
        shell.setConf(conf);

        String[] args = {"-text","/user/hello.txt"};   // 普通目录
        int res;
        try {
            res = ToolRunner.run(shell, args);
        } finally {
            shell.close();
        }
        System.exit(res);
    }
}

测试类中直接调用ToolRunner.run,后续过程同FsShell类调用过程。

我们先看一下 hadoop fs -text 的调用栈:

"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at org.apache.hadoop.hdfs.DistributedFileSystem.open(DistributedFileSystem.java:353)
	  at org.apache.hadoop.fs.FileSystem.open(FileSystem.java:837)
	  at org.apache.hadoop.fs.shell.Display$Cat.getInputStream(Display.java:114)
	  at org.apache.hadoop.fs.shell.Display$Text.getInputStream(Display.java:131)
	  at org.apache.hadoop.fs.shell.Display$Cat.processPath(Display.java:102)
	  at org.apache.hadoop.fs.shell.Command.processPaths(Command.java:319)
	  at org.apache.hadoop.fs.shell.Command.processPathArgument(Command.java:291)
	  at org.apache.hadoop.fs.shell.Command.processArgument(Command.java:273)
	  at org.apache.hadoop.fs.shell.Command.processArguments(Command.java:257)
	  at org.apache.hadoop.fs.shell.Command.processRawArguments(Command.java:203)
	  at org.apache.hadoop.fs.shell.Command.run(Command.java:167)
	  at org.apache.hadoop.fs.FsShell.run(FsShell.java:287)
	  at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
	  at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:84)
	  at cn.whbing.hadoop.FsShellTest.main(FsShellTest.java:25)

在这里插入图片描述
我们看到处理流程:

ToolRunner.run --> FsShell.run --> Command.processPaths --> Display$Cat.processPath

至此参数解析完毕,通过传入的参数解析到为 read 操作。此后就是open等操作,可以看做 read 操作的开始,分析如下。

附:
对于读的断点调试,我们还可以直接在客户端调用open方法来测试,如:

    /**
     * 测试2:直接调用FileSystem的open方法来读
     */
    @Test
    public void testRead(){
        try {
            Configuration conf = new Configuration();
            conf.set("fs.defaultFS","hdfs://hadoop1:9000");
            FileSystem fs = FileSystem.get(conf);
            Path path = new Path("/user/hello.txt");
            FSDataInputStream in = fs.open(path);
            BufferedReader buff = new BufferedReader(new InputStreamReader(in));
            String str = null;
            while ((str = buff.readLine()) != null){
                System.out.println(str);
            }
            buff.close();
            in.close();
        } catch (Exception e){

        }
    }

2. 读操作分析

1. 客户端打开文件流

如直接使用上述java调用:对于 FSDataInputStream in = fs.open(path);断点调试,其调用栈为:

"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at org.apache.hadoop.hdfs.DistributedFileSystem.open(DistributedFileSystem.java:353)
	  at org.apache.hadoop.fs.FileSystem.open(FileSystem.java:837)
	  at cn.whbing.hadoop.FsShellTest.testRead(FsShellTest.java:57)

调用了DistributedFileSystem.open。
如使用shell调用:入口代码位于Display$Cat类中:

    protected InputStream getInputStream(PathData item) throws IOException {
      return item.fs.open(item.path);
    }

这里的fs就是DistributedFileSystem。可以看到java调用和shell调用,在这里重合,后续用同一套代码。

1. DistributedFileSystem.open
@Override
  public FSDataInputStream open(Path f, final int bufferSize)
      throws IOException {
    statistics.incrementReadOps(1);
    Path absF = fixRelativePart(f);
    return new FileSystemLinkResolver<FSDataInputStream>() {
      @Override
      public FSDataInputStream doCall(final Path p)
          throws IOException, UnresolvedLinkException {
        final DFSInputStream dfsis =
          dfs.open(getPathName(p), bufferSize, verifyChecksum);
        return dfs.createWrappedInputStream(dfsis);
      }
      @Override
      public FSDataInputStream next(final FileSystem fs, final Path p)
          throws IOException {
        return fs.open(p, bufferSize);
      }
    }.resolve(this, absF);
  }

上述的关键代码在于:

DFSInputStream dfsis =
          dfs.open(getPathName(p), bufferSize, verifyChecksum);

其调用了DFSClient.open,对于dfs.createWrappedInputStream(dfsis)得到的FSDataInputStream也只是包装类。真正操作的还是DFSInputStream。
在这里插入图片描述
hadoop封装了InputStream。

2. DFSClient.open得到DFSInputStream
  public DFSInputStream open(String src, int buffersize, boolean verifyChecksum)
      throws IOException, UnresolvedLinkException {
    //检查客户端读取文件是否关闭
    checkOpen();
    //    Get block info from namenode
    TraceScope scope = getPathTraceScope("newDFSInputStream", src);
    try {
      // 返回DFSInputStream的具体逻辑在构造器中
      return new DFSInputStream(this, src, verifyChecksum);
    } finally {
      scope.close();
    }
  }

可知,open的过程就是创建对象的过程。

3. DFSInputStream构造器
  DFSInputStream(DFSClient dfsClient, String src, boolean verifyChecksum,
                 LocatedBlocks locatedBlocks) throws IOException {
    this.dfsClient = dfsClient;
    this.verifyChecksum = verifyChecksum;
    this.src = src;
    synchronized (infoLock) {
      // 读缓存策略
      this.cachingStrategy = dfsClient.getDefaultReadCachingStrategy();
    }
    this.locatedBlocks = locatedBlocks;
    //读取block信息
    openInfo(false); //由info改成了info(false)
  }

上述使用的读缓存策略。
读缓存策略,readDropBehind和readahead两个参数控制读缓存策略,数据读取通常为磁盘操作,每次read将会读取一页数据(512b或者更大),这些数据加载到内存并传输给Client。
readDropBehind表示读后即弃,即数据读取后立即丢弃cache数据,这可以在多用户并发文件读取时有效节约内存,不过会导致更频繁的磁盘操作,
如果关闭此特性,read操作后数据会被cache在内存,对于同一个文件的多次读取可以有效的提升性能,但会消耗更多内存。readahead为预读,
如果开启,那么Datanode将会在一次磁盘读取操作中向前额外的多读取一定字节的数据,在线性读取时,这可以有效降低IO操作延迟。
这个特性需要在Datanode上开启Native libaries,否则不会生效。

4. DFSInputStream.openInfo()
/**
   * Grab the open-file info from namenode
   */
  void openInfo() throws IOException, UnresolvedLinkException {
    synchronized(infoLock) {
      // 获取block块信息,并获得最后一个block的长度
      lastBlockBeingWrittenLength = fetchLocatedBlocksAndGetLastBlockLength();
      int retriesForLastBlockLength = dfsClient.getConf().retryTimesForGetLastBlockLength;
      while (retriesForLastBlockLength > 0) {
        // Getting last block length as -1 is a special case. When 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 for 3 times to get the length.
        if (lastBlockBeingWrittenLength == -1) {
          DFSClient.LOG.warn("Last block locations not available. "
              + "Datanodes might not have reported blocks completely."
              + " Will retry for " + retriesForLastBlockLength + " times");
          waitFor(dfsClient.getConf().retryIntervalForGetLastBlockLength);
          lastBlockBeingWrittenLength = fetchLocatedBlocksAndGetLastBlockLength();
        } else {
          break;
        }
        retriesForLastBlockLength--;
      }
      if (retriesForLastBlockLength == 0) {
        throw new IOException("Could not obtain the last block locations.");
      }
    }
  }

获取block块信息,并获得最后一个block的长度。
在这里插入图片描述在这里插入图片描述

5. DFSInputStream.fetchLocatedBlocksAndGetLastBlockLength()
 private long fetchLocatedBlocksAndGetLastBlockLength() throws IOException {
    //客户端向namenode请求获取block信息,这里通过调用dfsClient的方法来执行
    final LocatedBlocks newInfo = dfsClient.getLocatedBlocks(src, 0);
    if (DFSClient.LOG.isDebugEnabled()) {
      DFSClient.LOG.debug("newInfo = " + newInfo);
    }
    if (newInfo == null) {
      throw new IOException("Cannot open filename " + src);
    }

    // 重试3次,在后边重试的3次中,确保数据一致,如果有改动抛出异常
    if (locatedBlocks != null) {
      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!");
        }
      }
    }
    locatedBlocks = newInfo;
    long lastBlockBeingWrittenLength = 0;
    if (!locatedBlocks.isLastBlockComplete()) {
      final LocatedBlock last = locatedBlocks.getLastLocatedBlock();
      if (last != null) {
        if (last.getLocations().length == 0) {
          if (last.getBlockSize() == 0) {
            // if the length is zero, then no data has been written to
            // datanode. So no need to wait for the locations.
            return 0;
          }
          return -1;
        }
        final long len = readBlockLength(last);
        last.getBlock().setNumBytes(len);
        lastBlockBeingWrittenLength = len; 
      }
    }

    fileEncryptionInfo = locatedBlocks.getFileEncryptionInfo();

    return lastBlockBeingWrittenLength;
  }

LocatedBlocks 数据结构在后续分析。
上述代码有两次RPC调用:

  • 获取LocatedBlocks,并得到最后一个块信息。
  • 获得块的长度
    在这里插入图片描述
    在这里插入图片描述

整个DFSClient.open调用过程如下:
在这里插入图片描述

6. 获取block信息DFSClient.getLocatedBlocks()
  public LocatedBlocks getLocatedBlocks(String src, long start)
      throws IOException {
    return getLocatedBlocks(src, start, dfsClientConf.prefetchSize);
  }
  public LocatedBlocks getLocatedBlocks(String src, long start, long length)
      throws IOException {
    TraceScope scope = getPathTraceScope("getBlockLocations", src);
    try {
      return callGetBlockLocations(namenode, src, start, length);
    } finally {
      scope.close();
    }
  }
7. 远程RPC调用DFSClient.callGetBlockLocations()
  /**
   * @see ClientProtocol#getBlockLocations(String, long, long)
   */
  static LocatedBlocks callGetBlockLocations(ClientProtocol namenode,
      String src, long start, long length) 
      throws IOException {
    try {
      //通过ClientProtocol(ClientNamenodeProtocolTranslatorPB)的协议向namenode请求
      return namenode.getBlockLocations(src, start, length);
    } catch(RemoteException re) {
      throw re.unwrapRemoteException(AccessControlException.class,
                                     FileNotFoundException.class,
                                     UnresolvedPathException.class);
    }
  }
8. RPC ClientProtocal.getBlockLocations 的实现ClientNamenodeProtocolTranslatorPB.getBlockLocations
@Override
  public LocatedBlocks getBlockLocations(String src, long offset, long length)
      throws AccessControlException, FileNotFoundException,
      UnresolvedLinkException, IOException {
    GetBlockLocationsRequestProto req = GetBlockLocationsRequestProto
        .newBuilder()
        .setSrc(src)
        .setOffset(offset)
        .setLength(length)
        .build();
    try {
      // rpc调用暂时不分析了
      //调用NameNodeRpcServer.getBlockLocations
      //rpcProxy: localhost/127.0.0.1:51397, ProtobufRpcEngine, ClientNamenodeProtocolPB
      GetBlockLocationsResponseProto resp = rpcProxy.getBlockLocations(null,
          req);
      return resp.hasLocations() ? 
        PBHelper.convert(resp.getLocations()) : null;
    } catch (ServiceException e) {
      throw ProtobufHelper.getRemoteException(e);
    }
  }
(namenode RPC获取block信息见其他)

本节内容鉴于篇幅,单独写。

2. 读操作–DFSInputStream实现

上述DFSClient.open后,返回了HdfsDataInputStream类型(继承了FSDataInputStream、DataInputStream),这是一个封装的类型。
实际操作的类型是DFSInputStream。
之后就可以调用HdfsDataInputStream.read进行读操作了。

通过shell调试获得客户端读操作的调用栈:

"main@1" prio=5 tid=0x1 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at org.apache.hadoop.hdfs.DFSInputStream.readWithStrategy(DFSInputStream.java:874)
	  - locked <0xacb> (a org.apache.hadoop.hdfs.DFSInputStream)
	  at org.apache.hadoop.hdfs.DFSInputStream.read(DFSInputStream.java:940)
	  at org.apache.hadoop.hdfs.DFSInputStream.read(DFSInputStream.java:741)
	  at java.io.DataInputStream.readShort(DataInputStream.java:312)
	  at org.apache.hadoop.fs.shell.Display$Text.getInputStream(Display.java:136)
	  at org.apache.hadoop.fs.shell.Display$Cat.processPath(Display.java:102)
	  at org.apache.hadoop.fs.shell.Command.processPaths(Command.java:319)
	  at org.apache.hadoop.fs.shell.Command.processPathArgument(Command.java:291)
	  at org.apache.hadoop.fs.shell.Command.processArgument(Command.java:273)
	  at org.apache.hadoop.fs.shell.Command.processArguments(Command.java:257)
	  at org.apache.hadoop.fs.shell.Command.processRawArguments(Command.java:203)
	  at org.apache.hadoop.fs.shell.Command.run(Command.java:167)
	  at org.apache.hadoop.fs.FsShell.run(FsShell.java:287)
	  at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
	  at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:84)
	  at cn.whbing.hadoop.ReadWriteTest.read1(ReadWriteTest.java:43)

以下是shell方式调试,Display类获得DFSInputStream后的处理。

    @Override
    protected InputStream getInputStream(PathData item) throws IOException {
      FSDataInputStream i = (FSDataInputStream)super.getInputStream(item);

      // Handle 0 and 1-byte files
      short leadBytes;
      try {
        // readShort读取开头的两个字节,排列在高低位组成short
        leadBytes = i.readShort();
      } catch (EOFException e) {
        i.seek(0);
        return i;
      }

      // Check type of stream first
      switch(leadBytes) {
        case 0x1f8b: { // RFC 1952
          // Must be gzip
          i.seek(0);
          return new GZIPInputStream(i);
        }
        case 0x5345: { // 'S' 'E'
          // Might be a SequenceFile
          if (i.readByte() == 'Q') {
            i.close();
            return new TextRecordInputStream(item.stat);
          }
        }
        default: {
          // Check the type of compression instead, depending on Codec class's
          // own detection methods, based on the provided path.
          CompressionCodecFactory cf = new CompressionCodecFactory(getConf());
          CompressionCodec codec = cf.getCodec(item.path);
          if (codec != null) {
            i.seek(0);
            return codec.createInputStream(i);
          }
          break;
        }
        case 0x4f62: { // 'O' 'b'
          if (i.readByte() == 'j') {
            i.close();
            return new AvroFileInputStream(item.stat);
          }
          break;
        }
      }

      // File is non-compressed, or not a file container we know.
      i.seek(0);
      return i;
    }
  }    

根据DataInputStream.readShort方法取到流的前两个字节,根据前两个字节来判断类型。如普通文本,会走到default中,default中会检查是否压缩。

  • 如果压缩,seek(0)并返回解压缩。
  • 如果不压缩,seek(0)并返回。

readShort:

    public final short readShort() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (short)((ch1 << 8) + (ch2 << 0));
    }

看子类read的实现,i的实际类型是 HdfsDataInputStream --> FSDataInputStream --> DataInputStream。
这是包装类,我们看DFSInputStream的read实现。

DFSInputStream.read:

  // 读一个字节
  public synchronized int read() throws IOException {
    if (oneByteBuf == null) {
      oneByteBuf = new byte[1];
    }
    int ret = read( oneByteBuf, 0, 1 );
    return ( ret <= 0 ) ? -1 : (oneByteBuf[0] & 0xff);
  }
  //
  @Override
  public synchronized int read(final byte buf[], int off, int len) throws IOException {
    // 使用字节数组作为容器
    ReaderStrategy byteArrayReader = new ByteArrayStrategy(buf);
    TraceScope scope =
        dfsClient.getPathTraceScope("DFSInputStream#byteArrayRead", src);
    try {
      return readWithStrategy(byteArrayReader, off, len);
    } finally {
      scope.close();
    }
  }

其中read方法调用 readWithStrategy

DFSInputStream.readWithStrategy


  private synchronized int readWithStrategy(ReaderStrategy strategy, int off, int len) throws IOException {
    dfsClient.checkOpen();
    if (closed.get()) {
      throw new IOException("Stream closed");
    }
    Map<ExtendedBlock,Set<DatanodeInfo>> corruptedBlockMap 
      = new HashMap<ExtendedBlock, Set<DatanodeInfo>>();
    failures = 0;
    if (pos < getFileLength()) {
      int retries = 2;
      while (retries > 0) {
        try {
          // currentNode can be left as null if previous read had a checksum
          // error on the same block. See HDFS-3067
          if (pos > blockEnd || currentNode == null) {
            currentNode = blockSeekTo(pos);
          }
          int realLen = (int) Math.min(len, (blockEnd - pos + 1L));
          synchronized(infoLock) {
            if (locatedBlocks.isLastBlockComplete()) {
              realLen = (int) Math.min(realLen,
                  locatedBlocks.getFileLength() - pos);
            }
          }
          int result = readBuffer(strategy, off, realLen, corruptedBlockMap);
          
          if (result >= 0) {
            pos += result;
          } else {
            // got a EOS from reader though we expect more data on it.
            throw new IOException("Unexpected EOS from the reader");
          }
          if (dfsClient.stats != null) {
            dfsClient.stats.incrementBytesRead(result);
          }
          return result;
        } catch (ChecksumException ce) {
          throw ce;            
        } catch (IOException e) {
          if (retries == 1) {
            DFSClient.LOG.warn("DFS Read", e);
          }
          blockEnd = -1;
          if (currentNode != null) { addToDeadNodes(currentNode); }
          if (--retries == 0) {
            throw e;
          }
        } finally {
          // Check if need to report block replicas corruption either read
          // was successful or ChecksumException occured.
          reportCheckSumFailure(corruptedBlockMap, 
              currentLocatedBlock.getLocations().length);
        }
      }
    }
    return -1;
  }

private synchronized DatanodeInfo blockSeekTo(long target) 方法,
在这里插入图片描述

  private DNAddrPair chooseDataNode(LocatedBlock block,
      Collection<DatanodeInfo> ignoredNodes) throws IOException {
    while (true) {
      try {
        return getBestNodeDNAddrPair(block, ignoredNodes);
      } catch (IOException ie) {
        String errMsg = getBestNodeDNAddrPairErrorString(block.getLocations(),
          deadNodes, ignoredNodes);
        String blockInfo = block.getBlock() + " file=" + src;
        if (failures >= dfsClient.getMaxBlockAcquireFailures()) {
          String description = "Could not obtain block: " + blockInfo;
          DFSClient.LOG.warn(description + errMsg
              + ". Throwing a BlockMissingException");
          throw new BlockMissingException(src, description,
              block.getStartOffset());
        }

getBestNodeDNAddrPair 的方法如下:
在这里插入图片描述
Q:
这个true 出不来怎么办?

private DNAddrPair chooseDataNode(LocatedBlock block,
Collection ignoredNodes) throws IOException {
while (true) {
try {


根据错误提示:

hadoop fs -text hdfs://hadoop1:9000/ec/t.log

19/08/14 17:24:08 WARN hdfs.DFSClient: Failed to connect to /10.179.17.22:9866 for block, add to deadNodes and continue. java.io.IOException: Got error, status message opReadBlock BP-1712821023-10.179.25.59-1564737285129:blk_-9223372036854775648_1019 received exception org.apache.hadoop.hdfs.server.datanode.ReplicaNotFoundException: Replica not found for BP-1712821023-10.179.25.59-1564737285129:blk_-9223372036854775648_1019, for OP_READ_BLOCK, self=/10.179.72.122:47858, remote=/10.179.17.22:9866, for file /ec/t.log, for pool BP-1712821023-10.179.25.59-1564737285129 block -9223372036854775648_1019
java.io.IOException: Got error, status message opReadBlock BP-1712821023-10.179.25.59-1564737285129:blk_-9223372036854775648_1019 received exception org.apache.hadoop.hdfs.server.datanode.ReplicaNotFoundException: Replica not found for BP-1712821023-10.179.25.59-1564737285129:blk_-9223372036854775648_1019, for OP_READ_BLOCK, self=/10.179.72.122:47858, remote=/10.179.17.22:9866, for file /ec/t.log, for pool BP-1712821023-10.179.25.59-1564737285129 block -9223372036854775648_1019
        at org.apache.hadoop.hdfs.protocol.datatransfer.DataTransferProtoUtil.checkBlockOpStatus(DataTransferProtoUtil.java:142)
        at org.apache.hadoop.hdfs.RemoteBlockReader2.checkSuccess(RemoteBlockReader2.java:456)
        at org.apache.hadoop.hdfs.RemoteBlockReader2.newBlockReader(RemoteBlockReader2.java:424)
        at org.apache.hadoop.hdfs.BlockReaderFactory.getRemoteBlockReader(BlockReaderFactory.java:818)
        at org.apache.hadoop.hdfs.BlockReaderFactory.getRemoteBlockReaderFromTcp(BlockReaderFactory.java:697)
        at org.apache.hadoop.hdfs.BlockReaderFactory.build(BlockReaderFactory.java:355)
        at org.apache.hadoop.hdfs.DFSInputStream.blockSeekTo(DFSInputStream.java:679)
        at org.apache.hadoop.hdfs.DFSInputStream.readWithStrategy(DFSInputStream.java:888)
        at org.apache.hadoop.hdfs.DFSInputStream.read(DFSInputStream.java:940)
        at org.apache.hadoop.hdfs.DFSInputStream.read(DFSInputStream.java:741)
        at java.io.DataInputStream.readShort(DataInputStream.java:312)
        at org.apache.hadoop.fs.shell.Display$Text.getInputStream(Display.java:136)
        at org.apache.hadoop.fs.shell.Display$Cat.processPath(Display.java:102)
        at org.apache.hadoop.fs.shell.Command.processPaths(Command.java:317)
        at org.apache.hadoop.fs.shell.Command.processPathArgument(Command.java:289)
        at org.apache.hadoop.fs.shell.Command.processArgument(Command.java:271)
        at org.apache.hadoop.fs.shell.Command.processArguments(Command.java:255)
        at org.apache.hadoop.fs.shell.Command.processRawArguments(Command.java:201)
        at org.apache.hadoop.fs.shell.Command.run(Command.java:165)
        at org.apache.hadoop.fs.FsShell.run(FsShell.java:287)
        at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
        at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:84)
        at org.apache.hadoop.fs.FsShell.main(FsShell.java:340)

对于 BlockReader:

public interface BlockReader extends ByteBufferReadable { 
	  int read(byte[] buf, int off, int len) throws IOException;
      long skip(long n) throws IOException;
      ...
}

getRemoteBlockReaderFromTcp

  private BlockReader getRemoteBlockReader(Peer peer) throws IOException {
    if (conf.useLegacyBlockReader) {
      return RemoteBlockReader.newBlockReader(fileName,
          block, token, startOffset, length, conf.ioBufferSize,
          verifyChecksum, clientName, peer, datanode,
          clientContext.getPeerCache(), cachingStrategy);
    } else {
      return RemoteBlockReader2.newBlockReader(
          fileName, block, token, startOffset, length,
          verifyChecksum, clientName, peer, datanode,
          clientContext.getPeerCache(), cachingStrategy);
    }
  }

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

readBlock方法中,通过发送值为81的状态码org.apache.hadoop.hdfs.protocol.datatransfer.Op.READ_BLOCK 到DataXceiver中的peer服务。
在这里插入图片描述
在这里我们看到把各个参数都set到了OpReadBlockProto对象里,然后发送出去,也就是发送到了初始化的DataXceiverServer服务.

这个时候服务端一直阻塞的socket线程将会收到操作码为81的读请求,然后就进行后续的处理

我们看下,其实其他的一些对于数据的操作,如copyBlock,writeBlock都是在Sender中完成的.

Sender的包是 public class Sender implements DataTransferProtocol

我们来看DataXceiverServer的run方法。

 public void run() {
    Peer peer = null;
    while (datanode.shouldRun && !datanode.shutdownForUpgrade) {
      try {
       //通过accept方法在这里一直阻塞,直到有请求过来,我们通过跟踪代码,看到内部其实是封装了java的serverSocket的accept方法.
        peer = peerServer.accept();

        // Make sure the xceiver count is not exceeded
        int curXceiverCount = datanode.getXceiverCount();
        if (curXceiverCount > maxXceiverCount.get()) {
          throw new IOException("Xceiver count " + curXceiverCount
              + " exceeds the limit of concurrent xcievers: "
              + maxXceiverCount.get());
        }
        
      //当有请求过来的时候,就通过DataXceiver.create创建了一个守护进程,并将其加到线程组里.
        new Daemon(datanode.threadGroup,
            DataXceiver.create(peer, datanode, this))
            .start();
      } catch (SocketTimeoutException ignored) {
        // wake up to see if should continue to run
      } 
      ...
  }

通过 op = readOp();获取具体是什么操作,读、写、copy等,然后processOp(op);方法来处理具体的逻辑

在方法中,通过switch来具体的分发,让不同的方法执行不同的逻辑

protected final void processOp(Op op) throws IOException {
    switch(op) {
    case READ_BLOCK:
      opReadBlock();
      break;
    case WRITE_BLOCK:
      opWriteBlock(in);
      break;
    case REPLACE_BLOCK:
      opReplaceBlock(in);
      break;
    case COPY_BLOCK:
      opCopyBlock(in);
      break;
    case BLOCK_CHECKSUM:
      opBlockChecksum(in);
      break;
    case TRANSFER_BLOCK:
      opTransferBlock(in);
      break;
    case REQUEST_SHORT_CIRCUIT_FDS:
      opRequestShortCircuitFds(in);
      break;
    case RELEASE_SHORT_CIRCUIT_FDS:
      opReleaseShortCircuitFds(in);
      break;
    case REQUEST_SHORT_CIRCUIT_SHM:
      opRequestShortCircuitShm(in);
      break;
    default:
      throw new IOException("Unknown op " + op + " in data stream");
    }
  }

参考:

https://zhangjun5965.iteye.com/blog/2375278

HdfsClient-在本地开发环境使用Java远程连接操作HDFS 一、pom.xml配置 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apac 阅读详情

相关推荐

HDFS Standby NameNode Read功能剖析

文章目录前言HDFS Standby Read的背景及功能要求Standby NameNode一致性读的控制实现原理分析代码分析流程分析图参考链接 前言 HDFS有着一套十分成熟的HA的机制来保证其服务的高可用性。在HA模式下,分别对应有Active和Standby NameNode的服务。Active NameNode用于提供对外数据服务,而Standby NameNode则负责做checkpoint的工作以及随时准备接替变成Active NameNode的角色,假设说当前Active NameNode

走在前往架构师的路上 2192

HDFS知识点梳理

一、定义 hdfs是hadoop生态中用来存取海量大数据的分布式的文件存储系统,通过目录树来定位文件. 二、适用场景 适合一次写入,多次读出的场景,不支持修改,但是支持append操作。适合大数据分析工作,但是步骤繁琐,不适合用来当网盘使用,且不适用于小文件过多的场景☆ 三、优缺点 优点:1.高容错,2.适合大数据,3.可构建于廉价机器 缺点:1.不适用于低延迟的场景(毫秒级),2.无法高效的对小文件进行操作,3.只支持写入,不支持修改,但是支持追加。 四、hdfs中client的作用 1.

weixin_45937224的博客 1454

后端分布式系列:分布式存储-HDFS 架构解析

本文以 Hadoop 提供的分布式文件系统(HDFS)为例来进一步展开解析分布式存储服务架构设计的要点。架构目标任何一种软件框架或服务都是为了解决特定问题而产生的。还记得我们在 《分布式存储 - 概述》一文中描述的几个关注方面么?分布式文件系统属于分布式存储中的一种面向文件的数据模型,它需要解决单机文件系统面临的容量扩展和容错问题。所以 HDFS 的架构设计目标就呼之欲出了: 面向超大文件或大量的文

瞬息之间 6724

hdfs-client 客户端连接

在pom.xml中导入相关依赖包:hadoop_hdfs,hadoop_common,hadoop_client。在resources中创建log4j.properties文件控制日志数量。在maven resporitry官网。切记:本地目录不能带中文名字会出错。4.在idle创建maven项目。在web_url中验证是否创建。给hdfs目录所有权限可读可写。也可以导入相关日志管理。在项目中创建Java。

qq_36012085的博客 1150

HDFS Multiple Standby的优化实践

在前面的文章中,笔者介绍过一篇关于介绍HDFS Multiple Standby功能的原理文章。从代码改动来看,HDFS Multiple Standby并没有对NameNode的核心机制做大的改动,而是进行了多Standby模式的一个适配。关于Multiple Standby这个功能本身,它确实能够增强NameNode服务的整体可靠性。假设我们使用

走在前往架构师的路上 830

Hadoop基础——HDFS知识点梳理

NameNode,DataNodeSecondary NameNode( HA模式下是 StandBy NameNode)Client: 客户端文件切分,文件上传HDFS时,client将文件切分成一个一个的block,然后进行存储。与NN交互,获取文件的位置信息。与DN交互,读取或者写入数据。Client提供一些命令来管理HDFS, 比如启动或者关闭HDFSClient可以通过一些命令来访问HDFS管理hdfs的命名空间管理数据块的映射信息。

YuannaY的博客 1204

HDFS Client Read流程分析【链接及图解】

1. 推荐博客: HDFS read解析(一)之Open文件流 HDFS read解析2之从文件流中read 打开文件 读操作 打开文件 当客户端读取一个HDFS文件时,首先会调用DistributedFileSystem.open()方法打开这个文件,open方法首先会调用DFSCklient.open()方法创建HDFS文件对应的DFSInputStream输入流对象,然后构建一个...

王小禾 586

【HDFS】简要了解HDFS的预读(read-ahead)机制

【HDFS】简要了解HDFS的预读(read-ahead)机制

LifeIsGood 4578

HDFS Client Read流程分析[hadoop3.2与2.7.2对比]

说明 hadoop2.7.2的Read流程见:HDFS Client Read流程分析 直接将2.7.2的版本换成3.2.0,可能会报No FileSystem for scheme "hdfs" ,原因是需要加入hdfs相关的3个依赖: <dependency> <groupId>org.apache.hadoop</groupId> &lt...

王小禾 417

三极管开关电路在脉冲应用中的性能瓶颈分析

深入探讨三极管开关电路在高频脉冲场景下的响应延迟与功耗问题,揭示其性能限制的关键因素。结合实际应用,分析开关速度与信号完整性之间的矛盾,为优化三极管开关电路设计提供实用参考。

weixin_42602368的博客 587

ThingsBoard入门实战(七):公共发布和 UI 细节修改

公共发布和 UI 细节修改7.细节修改和公共发布一、公共发布二、UI优化下载运行修改三、下一步还可以做什么?更好的UI更丰富的系统模块 7.细节修改和公共发布 GET到新技能了,想给兄弟们演示,需要给他们每个人一个账号么? 答案肯定是:不需要! ThingsBoard 用 公共发布 解决了这个问题。 一、公共发布 首先我们来看一下路灯设备现在在谁名下? 如下图,路灯都是customerA的, 目前只有它和它的租户可以看到,其他人需要看就需要设为公有。 如图操作: 对于设备详情仪表盘,我们也需要设为公有

AIGC搞起 9116

HDFS Hedged Read

Hedged reads是HDFS的一个功能,在Hadoop 2.4.0之后引入。一般来说,每个读请求都会由生成的一个线程处理。在Hedged reads 启用后,客户端可以等待一个预配置的时间,如果read没有返回,则客户端会生成第二个读请求,访问同一份数据的另一个block replica之后,其中任意一个read 先返回的话,则另一个read请求则被丢弃。 Hedged reads使用的场景是:解决少概率的slow read(可能由瞬时错误导致,例如磁盘错误或是网络抖动等)。 客户端hedged读两个

王小禾 2336

众灵科技24路舵机控制器二次开发实战(1)-STM32串口指令控制详解

本文详细介绍了如何通过STM32单片机对众灵科技24路舵机控制器进行二次开发,实现串口指令控制。文章从硬件连接、通信协议解析到STM32代码实战,手把手指导开发者打通控制链路,并重点讲解了如何调用预设动作组驱动六足机器人,为后续集成传感器、无线遥控等高级功能奠定基础。

weixin_29188997的博客 483

HDFS读取流程

hdfs文件读取

weixin_72036027的博客 592

扫地机器人解决方案:工作原理与硬件构成

房间里柜子,鞋子,床,座椅,人都在什么地方?3、数字信号处理器。这主要是为控制芯片进行路径规划后,下发指令给执行的电机,包括舵轮(扫地机人前进,转向的轮子),扫地刷的电机,吸尘器的电机,让这些电机工作。扫地机器人的大脑主要用来规划路径,避障,以及人机交互,例如:人类设定打扫范围,以及语音指导扫地机器人进行打扫,都需要处理器进行处理后,将指令下发到执行机构。小脑属于跟执行层最近的控制区域,例如我们上面说的,后面两个处理器:数字信号处理器和微控制器都是对执行层(电机),进行控制和回馈外部信息的处理器。

迷途 7395

【网络安全】Python代码审计高危函数与安全审计建议

Python 灵活且动态的特性使得开发便捷,但同时也带来了严重安全隐患。如果开发者缺乏安全意识,极易引入命令执行、反序列化漏洞及模板注入漏洞。本文总结了 Python 中常见的高危函数、模块和第三方库,并给出对应的审计建议及其原理解析,供安全研究人员和开发者参考。

等风来 416

HDFS Hedged Read代码分析

背景: Hedged Read参数: 1.dfs.client.hedged.read.threadpool.size 并发Hedged Read的线程池大小 2.dfs.client.hedged.read.threshold.millis 开启一个Hedged Read之前的等待时间(毫秒) hadoop2.7.2 DFSInputStream类 的 hedgedFet...

鲸鱼写程序的专栏 915

基于FPGA的AGC自适应增益控制系统开发

自动增益控制(AGC)是一种在接收信号过程中自动调整放大器增益的技术,以确保输出信号的幅度保持在一个恒定的水平。基于FPGA实现AGC系统具有高性能、低延迟和高度可配置的特点,广泛应用于无线通信、音频处理和雷达等领域。FPGA上的AGC系统提供了一种高效且灵活的方法来处理变化的信号环境。在实际应用中,通过合理的设计和优化,可以显著提高信号处理系统的鲁棒性和可靠性。

走向CTO的路上... 1694

HDFS-源码深度分析RPC机制

HDFS-源码深度分析RPC机制 一个分布式系统最重要的组件之一就是RPC协议,如何构建一套高性能的RPC协议就显得非常重要了,那么来看看HDFS的RPC协议是如何构建的吧。 1.原理剖析部分 通信流程 HDFS的RPC协议的序列化协议分为两种,第一种是Writerable,一种是Protobuf,前者是HDFS是HDFS自定义的,也是默认的序列化协议,要用Protobuf需要将参数进行包装和响应进行解析为接口返回的类型。本篇主要研究Protobuf协议,Writerable其实也是囊括在内的。 简要流程

尹忠政的博客 1613

23 中望CAD二次开发(ZRX C++)-自定义实体(二)

本文详细介绍了自定义图形实体,如何实现几何变换、捕捉、节点编辑,并介绍了CAD代理的概念。

cilvsuanna的博客 437

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

深入解析HDFS写入流程:管道机制与数据可靠性保障

在分布式文件系统HDFS中,数据写入是一个经过精心设计的复杂过程,其核心目标是在保证数据可靠性的同时实现高效传输。整个写入流程始于客户端与NameNode的交互,经过数据分块、管道传输、副本确认等多个关键环节,最终完成数据的持久化存储。这一过程充分体现了HDFS"一次写入、多次读取"的设计理念,以及面对大规模数据时的处理能力。

zuiyuelong的博客 1220

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Observer Node [SBN-READ] 原理及实现分析

Observer Node [SBN-READ] 原理及实现分析 概述 为了提升集群的吞吐、减轻Active Name负载压力,社区提出了Observer Read的设计。Observer Read是尽可能的将读请求发送到新加入集群的Observer节点来达到提升集群吞吐的目的。但是节点间元数据的同步是有一定有延时的,所以对于Observer读会存在数据一致性的问题。社区提出了元数据的一致性模型,解决了元数据同步的问题,基于此我们看一下社区的处理方式及对应的代码实现。 背景 HA架构中的HDFS

ying123lei的博客 1677

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

HDFS详解(架构设计、副本放置策略、读写流程、进程、常用命令等)

文章摘要: 本文系统介绍了Hadoop分布式文件系统(HDFS)的核心原理、架构设计与实践应用。HDFS基于主从架构(NameNode、DataNode、SecondaryNameNode),通过数据块(默认128MB)、多副本策略(机架感知)实现高容错与高吞吐,适合流式访问超大文件。详细解析了文件读写流程、副本放置逻辑、小文件压缩优化及PID管理问题,并汇总了HDFS常用命令。文章还对比了HDFS与云存储的优劣,探讨了纠删码等新技术趋势,为大数据开发运维人员提供了从理论到实践的完整指南,适用于Hadoop

ywl470812087的博客 10万+

深入解析HDFS读取流程:短路读优化、零拷贝技术与DFSClient本地缓存策略

在HDFS(Hadoop Distributed File System)的架构中,读取流程的设计直接决定了海量数据访问的效率。作为分布式文件系统的核心操作之一,HDFS的读取流程需要协调客户端、NameNode和多个DataNode的协作,同时兼顾数据本地性、网络传输效率和容错能力。理解这一流程的运作机制,是后续探讨短路读优化、零拷贝技术等高级特性的基础。HDFS读取流程示意图。

zuiyuelong的博客 1240

HDFS 客户端读写超时时间解析

背景 前段我们Hadoop集群磁盘使用率比较高,部分硬盘空间使用超过了70%,这部分服务器的DataNode服务读写负载就比较高,造成部分数据同步任务因为读写超时而失...

Viking的博客 5372
上一篇: HDFS namenode(1)
下一篇: vue入门记录
王小禾
博客等级 码龄11年 80粉丝 291原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值