某企业级hadoop源代码分析-3

来源:互联网 发布:摄像机ntp端口号 编辑:程序博客网 时间:2024/05/22 13:11

本文尝试分析某大型大数据解决方案公司企业级hadoop源代码,班门弄斧。

本篇文章的重点为hdfs block管理逻辑。

修改点3

@@ -1086,7 +1087,8 @@     if (!namesystem.isPopulatingReplQueues()) {       return;     }-    invalidateBlocks.add(block, datanode, true);+  //  invalidateBlocks.add(block, datanode, true);+    invalidateBlocks.add(block, datanode, false);   }

首先定位到修改的函数addToInvalidates,该函数将datanode指定的块儿添加到失效块儿数组中。下面为对应代码。

  /**   * Adds block to list of blocks which will be invalidated on specified   * datanode and log the operation   */  void addToInvalidates(final Block block, final DatanodeInfo datanode) {    if (!namesystem.isPopulatingReplQueues()) {      return;    }    invalidateBlocks.add(block, datanode, true);  }

invalidateBlocks用于保存等待删除的数据块副本集合,InvalidateBlocks中的副本来自于corruptReplicas和excessReplicateMap这两个集合。

此处修改主要是改动第三个参数,第三个参数主要控制是否输出log。

这里没增加一个失效快就减少了一次log输出。

log输出属于IO,IO的速度还是比较慢的,而且很容易形成瓶颈。

这里的修改效果需要根据失效块儿的数量级和其占总体IO的比例来看。

修改点4

   /**@@ -1285,15 +1287,23 @@     nodesToProcess = Math.min(nodes.size(), nodesToProcess);     int blockCnt = 0;-    for (DatanodeInfo dnInfo : nodes) {-      int blocks = invalidateWorkForOneNode(dnInfo);-      if (blocks > 0) {-        blockCnt += blocks;-        if (--nodesToProcess == 0) {-          break;-        }-      }+  +    List<DatanodeInfo> toProcess=new ArrayList();+    LOG.warn("begain to process invalidate block,number of node:"+nodesToProcess+" nodes:"+nodes+"");+    for(int nodeCnt = 0; nodeCnt < nodesToProcess; nodeCnt++ ) {+           toProcess.add(nodes.get(nodeCnt));     }+    blockCnt += invalidateWorkForOneNode(toProcess);+    +//    for (DatanodeInfo dnInfo : nodes) {+//      int blocks = invalidateWorkForOneNode(dnInfo);+//      if (blocks > 0) {+//        blockCnt += blocks;+//        if (--nodesToProcess == 0) {+//          break;+//        }+//      }+//    }     return blockCnt;   }@@ -3560,6 +3636,46 @@     return toInvalidate.size();   }+ +private int invalidateWorkForOneNode(List<DatanodeInfo> dn) {+     final List<Block> toInvalidate = new ArrayList();+       namesystem.writeLock();+       try {+         // blocks should not be replicated or removed if safe mode is on+         if (namesystem.isInSafeMode()) {+           LOG.debug("In safemode, not computing replication work");+           return 0;+         }+        for (DatanodeInfo dni : dn) {+         try {+               List ret = invalidateBlocks.invalidateWork(datanodeManager+                       .getDatanode(dni));+               if (NameNode.stateChangeLog.isInfoEnabled()) {+                   NameNode.stateChangeLog.info("BLOCK* "+                           + getClass().getSimpleName() + ": ask " + dn+                           + " to delete " + ret);+               }+               if (ret != null) {+                   toInvalidate.addAll(ret);+               }+           +           if (toInvalidate == null) {+             return 0;+           }+         } catch(UnregisteredNodeException une) {+           return 0;+         }+        }+       } finally {+         namesystem.writeUnlock();+       }+       if (blockLog.isInfoEnabled()) {+         blockLog.info("BLOCK* " + getClass().getSimpleName()+             + ": ask " + dn + " to delete " + toInvalidate);+       }+       return toInvalidate.size();+     }+   boolean isPlacementPolicySatisfied(Block b) {     List<DatanodeDescriptor> liveNodes = new ArrayList<DatanodeDescriptor>();     Collection<DatanodeDescriptor> corruptNodes = corruptReplicas

这个修改比较长,整体来看修改的是computeInvalidateWork方法,该方法主要处理失效块的删除。
它首先随机出一组datanode,然后遍历调用invalidateWorkForOneNode进行失效块的删除,最终返回删除块的数量。下面为computeInvalidateWork源码

  /**   * Schedule blocks for deletion at datanodes   * @param nodesToProcess number of datanodes to schedule deletion work   * @return total number of block for deletion   */  int computeInvalidateWork(int nodesToProcess) {    final List<DatanodeInfo> nodes = invalidateBlocks.getDatanodes();    Collections.shuffle(nodes);    nodesToProcess = Math.min(nodes.size(), nodesToProcess);    int blockCnt = 0;    for(int nodeCnt = 0; nodeCnt < nodesToProcess; nodeCnt++ ) {      blockCnt += invalidateWorkForOneNode(nodes.get(nodeCnt));    }    return blockCnt;  }
graph LRcomputeDatanodeWork-->computeInvalidateWorkReplicationMonitor-->computeDatanodeWork

BlockManager的ReplicationMonitor线程会定期(配置文件)执行删除操作,
每次删除时ReplicationMonitor线程都会从InvalidateBlocks中选出nodeToProcess个Datanode执行删除操作,
然后再从每个Datanode上选出limit个副本删除。

这里所做的修改主要针对invalidateWorkForOneNode方法,该方法原来只能进行一个节点的删除工作,
我们对该方法进行了重载,重载的方法可以为一次处理多个datanode节点。

那现在的区别在哪呢,我们先分析原版的invalidateWorkForOneNode方法。
该方法首先对namesystem加锁,然后判断namesystem是否处在安全模式,
如果没有处在安全模式,则调用invalidateBlocks.invalidateWork对dn进行实际的块删除工作。
删除完毕后,需要将namesystem解锁。

重载前后的主要区别在于加锁的次数,修改前每个dn处理单独加锁,修改后是集体加锁。
锁是有代价的,过于频繁的加锁会造成cpu的浪费。但是如果锁内的操作过长,也会影响其他线程的响应速度。

这里的修改可以通过调整一次处理的dn数目(配置文件),来达到加解锁的最优。
这里的点就是dn的数目如何来确定,(配置文件)

下面为方法源码。

  /**   * Get blocks to invalidate for <i>nodeId</i>   * in {@link #invalidateBlocks}.   *   * @return number of blocks scheduled for removal during this iteration.   */  private int invalidateWorkForOneNode(DatanodeInfo dn) {    final List<Block> toInvalidate;    namesystem.writeLock();    try {      // blocks should not be replicated or removed if safe mode is on      if (namesystem.isInSafeMode()) {        LOG.debug("In safemode, not computing replication work");        return 0;      }      try {        toInvalidate = invalidateBlocks.invalidateWork(datanodeManager.getDatanode(dn));        if (toInvalidate == null) {          return 0;        }      } catch(UnregisteredNodeException une) {        return 0;      }    } finally {      namesystem.writeUnlock();    }    if (NameNode.stateChangeLog.isInfoEnabled()) {      NameNode.stateChangeLog.info("BLOCK* " + getClass().getSimpleName()          + ": ask " + dn + " to delete " + toInvalidate);    }    return toInvalidate.size();  }

我们分析下重载后的invalidateWorkForOneNode方法,该方法首先对namesystem加锁,然后判断namesystem是否处在安全模式,如果没有处在安全模式,则遍历dn数组,调用invalidateBlocks.invalidateWork对dn进行实际的块删除工作。删除完毕后,需要将namesystem解锁。和
下面为重载后的源代码。

    /**     * Get blocks to invalidate for <i>nodeId</i> in {@link #invalidateBlocks}.     *     * @return number of blocks scheduled for removal during this iteration.     */    private int invalidateWorkForOneNode(List<DatanodeInfo> dn) {        final List<Block> toInvalidate = new ArrayList();        namesystem.writeLock();        try {            for (DatanodeInfo dni : dn) {                // blocks should not be replicated or removed if safe mode is on                if (namesystem.isInSafeMode()) {                    LOG.debug("In safemode, not computing replication work");                    return 0;                }                try {                    List ret = invalidateBlocks.invalidateWork(datanodeManager                            .getDatanode(dni));                    if (NameNode.stateChangeLog.isInfoEnabled()) {                        NameNode.stateChangeLog.info("BLOCK* "                                + getClass().getSimpleName() + ": ask " + dn                                + " to delete " + ret);                    }                    if (ret != null) {                        toInvalidate.addAll(ret);                    }                    if (toInvalidate == null) {                        return 0;                    }                } catch (UnregisteredNodeException une) {                    return 0;                }            }        } finally {            namesystem.writeUnlock();        }        return toInvalidate.size();    }

修改点5

@@ -2312,6 +2322,72 @@     }     return storedBlock;   }+  +  Random rand=new Random();+  /*+  enum ProcessType {TO_ADD,TO_INVA,TO_CORR,TO_UC,NULL};+   private BlockInfo processReportedBlock(final DatanodeDescriptor dn,+           final String storageID, final Block block,+           final ReplicaState reportedState, DatanodeDescriptor delHintNode)+           throws IOException {+       if (rand.nextInt() % 1000 == 0)+           if (LOG.isDebugEnabled()) {+               LOG.debug("Reported block " + block + " on " + dn + " size "+                       + block.getNumBytes() + " replicaState = "+                       + reportedState);+           }+       if (shouldPostponeBlocksFromFuture+               && namesystem.isGenStampInFuture(block)) {+            queueReportedBlock(storageInfo, block, reportedState,+                     QUEUE_REASON_FUTURE_GENSTAMP);+           return null;+       }++       // find block by blockId+       BlockInfo storedBlock = blocksMap.getStoredBlock(block);+       if (storedBlock == null) {+           Block nb = new Block(block);+           if (invalidateBlocks.blockSet.contains(nb) || rand.nextBoolean()) {+               return null;+           }+++           addToInvalidates(nb, dn);+           return null;+       }+       BlockUCState ucState = storedBlock.getBlockUCState();+++       if (invalidateBlocks.blockSet.contains(block)) {+           return storedBlock;+       }++       BlockToMarkCorrupt c = checkReplicaCorrupt(block, reportedState,+               storedBlock, ucState, dn);+       if (c != null) {+           if (shouldPostponeBlocksFromFuture) {+               queueReportedBlock(dn, storageID, storedBlock, reportedState,+                       QUEUE_REASON_CORRUPT_STATE);+           } else {+               markBlockAsCorrupt(c, dn, storageID);+           }+           return storedBlock;+       }+       if (isBlockUnderConstruction(storedBlock, ucState, reportedState)) {+           addStoredBlockUnderConstruction(new StatefulBlockInfo(+                   (BlockInfoUnderConstruction) storedBlock, block,+                   reportedState), dn, storageID);+           return storedBlock;+       }++       if (reportedState == ReplicaState.FINALIZED+               && (storedBlock.findDatanode(dn) < 0 || corruptReplicas+                       .isReplicaCorrupt(storedBlock, dn))) {+           addStoredBlock(storedBlock, dn, storageID, delHintNode, false);+       }+       return null;+   }+*/     /**    * Queue the given reported block for later processing in the

还有一个重写的方法为processReportedBlock,方法主要处理dn汇报的数据副本,
如果block不在blocksMap中,即nn不知道,则该块将标记为失效。
如果副本是合法的,即nn中可查到,则nn要记录副本位置。
如果副本不合法,则该副本被标记为corrupt,还会触发现有副本的复制工作。corrupt副本将在系统副本full后被清除。
如果副本是一个under construction的block,则副本会被添加到对应的建设队列。

。。。看错了,竟然是注释掉了,所以此处无修改。

最后

分析完毕,发现修改并不多。这不太像某企业的实例,或者说不太像某人的实例,找到原始cdh5.1.3的修改,发现修改确实比这多了不少,后续有时间再分析。

最后说下这些修改的号称效果。
这里写图片描述

对这个结果一定程度上,我是表示怀疑的,因为如果按照参数已经最优的同等条件来测,上面的那些修改可能无法带来这么大的提升。但是在某些特定环境下取得上述结果也还是可能的。

0 0