hadoop 2.6 源码 解读之追加写操作流程

33 篇文章 0 订阅
23 篇文章 2 订阅

客户端

DistributedFileSystem.java调用入口

 @Override
  public FSDataOutputStream append(Path f, final int bufferSize,
      final Progressable progress) throws IOException {
    statistics.incrementWriteOps(1);
    Path absF = fixRelativePart(f);
    return new FileSystemLinkResolver<FSDataOutputStream>() {
      @Override
      public FSDataOutputStream doCall(final Path p)
          throws IOException, UnresolvedLinkException {
        return dfs.append(getPathName(p), bufferSize, progress, statistics);
      }
      @Override
      public FSDataOutputStream next(final FileSystem fs, final Path p)
          throws IOException {
        return fs.append(p, bufferSize);
      }
    }.resolve(this, absF);
  }

调用 DFSClient

  public HdfsDataOutputStream append(final String src, final int buffersize,
      final Progressable progress, final FileSystem.Statistics statistics
      ) throws IOException {
    final DFSOutputStream out = append(src, buffersize, progress);
    return createWrappedOutputStream(out, statistics, out.getInitialLen());
  }

跳转至

  private DFSOutputStream append(String src, int buffersize, Progressable progress) 
      throws IOException {
    checkOpen();
    // 关键 在callAppend
    final DFSOutputStream result = callAppend(src, buffersize, progress);
    //获取租约
    beginFileLease(result.getFileId(), result);
    return result;
  }
callAppend
 private DFSOutputStream callAppend(String src,
      int buffersize, Progressable progress) throws IOException {
    LocatedBlock lastBlock = null;
    try {
    //获取文件最后一个数据块的位置信息,如果最后一个文件块已经写满,则返回 null
      lastBlock = namenode.append(src, clientName);
    } catch(RemoteException re) {
      throw re.unwrapRemoteException(AccessControlException.class,
                                     FileNotFoundException.class,
                                     SafeModeException.class,
                                     DSQuotaExceededException.class,
                                     UnsupportedOperationException.class,
                                     UnresolvedPathException.class,
                                     SnapshotAccessControlException.class);
    }
    HdfsFileStatus newStat = getFileInfo(src);
    //构造DFSOutputStream 对象
    return DFSOutputStream.newStreamForAppend(this, src, buffersize, progress,
        lastBlock, newStat, dfsClientConf.createChecksum());
  }
newStreamForAppend
 static DFSOutputStream newStreamForAppend(DFSClient dfsClient, String src,
      int buffersize, Progressable progress, LocatedBlock lastBlock,
      HdfsFileStatus stat, DataChecksum checksum) throws IOException {
    final DFSOutputStream out = new DFSOutputStream(dfsClient, src,
        progress, lastBlock, stat, checksum);
    out.start();
    return out;
  }
查看DFSOutputStream 构造
  /** Construct a new output stream for append. */
  private DFSOutputStream(DFSClient dfsClient, String src,
      Progressable progress, LocatedBlock lastBlock, HdfsFileStatus stat,
      DataChecksum checksum) throws IOException {
    this(dfsClient, src, progress, stat, checksum);
    initialFileSize = stat.getLen(); // length of file when opened

    Span traceSpan = null;
    if (Trace.isTracing()) {
      traceSpan = Trace.startSpan(this.getClass().getSimpleName()).detach();
    }

    // The last partial block of the file has to be filled.
    if (lastBlock != null) {
      // indicate that we are appending to an existing block
      bytesCurBlock = lastBlock.getBlockSize();
      streamer = new DataStreamer(lastBlock, stat, bytesPerChecksum, traceSpan);
    } else {
      computePacketChunkSize(dfsClient.getConf().writePacketSize, bytesPerChecksum);
      streamer = new DataStreamer(stat, traceSpan);
    }
    this.fileEncryptionInfo = stat.getFileEncryptionInfo();
  }

服务端

DFSClient 代码中
lastBlock = namenode.append(src, clientName);
最终通过RPC,调用 NameNodeRpcServer append 方法

 @Override // ClientProtocol
  public LocatedBlock append(String src, String clientName) 
      throws IOException {
    String clientMachine = getClientMachine();
    if (stateChangeLog.isDebugEnabled()) {
      stateChangeLog.debug("*DIR* NameNode.append: file "
          +src+" for "+clientName+" at "+clientMachine);
    }
    LocatedBlock info = namesystem.appendFile(src, clientName, clientMachine);
    metrics.incrFilesAppended();
    return info;
  }

最终调用到 FSNamesystem 的 appendFileInternal

appendFileInternal
private LocatedBlock appendFileInternal(FSPermissionChecker pc, String src,
      String holder, String clientMachine, boolean logRetryCache)
      throws AccessControlException, UnresolvedLinkException,
      FileNotFoundException, IOException {
    assert hasWriteLock();
    // Verify that the destination does not exist as a directory already.
    final INodesInPath iip = dir.getINodesInPath4Write(src);
    final INode inode = iip.getLastINode();
    //判断是不是目录
    if (inode != null && inode.isDirectory()) {
      throw new FileAlreadyExistsException("Cannot append to directory " + src
          + "; already exists as a directory.");
    }
    if (isPermissionEnabled) {
      checkPathAccess(pc, src, FsAction.WRITE);
    }

    try {
      if (inode == null) {
        throw new FileNotFoundException("failed to append to non-existent file "
          + src + " for client " + clientMachine);
      }
      INodeFile myFile = INodeFile.valueOf(inode, src, true);
      final BlockStoragePolicy lpPolicy =
          blockManager.getStoragePolicy("LAZY_PERSIST");

      if (lpPolicy != null &&
          lpPolicy.getId() == myFile.getStoragePolicyID()) {
        throw new UnsupportedOperationException(
            "Cannot append to lazy persist file " + src);
      }
      // 检查租约
      // Opening an existing file for write - may need to recover lease.
      recoverLeaseInternal(myFile, src, holder, clientMachine, false);

      // recoverLeaseInternal may create a new InodeFile via 
      // finalizeINodeFileUnderConstruction so we need to refresh 
      // the referenced file.  
      myFile = INodeFile.valueOf(dir.getINode(src), src, true);
      final BlockInfo lastBlock = myFile.getLastBlock();
      // Check that the block has at least minimum replication.
      //判断数据块是否有足够副本数
      if(lastBlock != null && lastBlock.isComplete() &&
          !getBlockManager().isSufficientlyReplicated(lastBlock)) {
        throw new IOException("append: lastBlock=" + lastBlock +
            " of src=" + src + " is not sufficiently replicated yet.");
      }
      return prepareFileForWrite(src, myFile, holder, clientMachine, true,
              iip.getLatestSnapshotId(), logRetryCache);
    } catch (IOException ie) {
      NameNode.stateChangeLog.warn("DIR* NameSystem.append: " +ie.getMessage());
      throw ie;
    }
  }
prepareFileForWrite实现
  LocatedBlock prepareFileForWrite(String src, INodeFile file,
                                   String leaseHolder, String clientMachine,
                                   boolean writeToEditLog,
                                   int latestSnapshot, boolean logRetryCache)
      throws IOException {
    file.recordModification(latestSnapshot);
    final INodeFile cons = file.toUnderConstruction(leaseHolder, clientMachine);

    leaseManager.addLease(cons.getFileUnderConstructionFeature()
        .getClientName(), src);

    LocatedBlock ret = blockManager.convertLastBlockToUnderConstruction(cons);
    if (ret != null) {
      // update the quota: use the preferred block size for UC block
      final long diff = file.getPreferredBlockSize() - ret.getBlockSize();
      dir.updateSpaceConsumed(src, 0, diff * file.getBlockReplication());
    }

    if (writeToEditLog) {
      getEditLog().logOpenFile(src, cons, false, logRetryCache);
    }
    return ret;
  }
prepareFileForWrite

功能是找到未写满的最后一个数据块

 LocatedBlock prepareFileForWrite(String src, INodeFile file,
                                   String leaseHolder, String clientMachine,
                                   boolean writeToEditLog,
                                   int latestSnapshot, boolean logRetryCache)
      throws IOException {
    file.recordModification(latestSnapshot);
    final INodeFile cons = file.toUnderConstruction(leaseHolder, clientMachine);

//添加租约
    leaseManager.addLease(cons.getFileUnderConstructionFeature()
        .getClientName(), src);
        // 获取最后一个数据块
    LocatedBlock ret = blockManager.convertLastBlockToUnderConstruction(cons);
    if (ret != null) {
      // update the quota: use the preferred block size for UC block
      final long diff = file.getPreferredBlockSize() - ret.getBlockSize();
      dir.updateSpaceConsumed(src, 0, diff * file.getBlockReplication());
    }

    if (writeToEditLog) {
      getEditLog().logOpenFile(src, cons, false, logRetryCache);
    }
    return ret;
  }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值