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

本文尝试分析某大型大数据解决方案公司企业级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 LR
computeDatanodeWork-->computeInvalidateWork
ReplicationMonitor-->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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值