【HDFS】hadoop的机架感知策略是啥?

了解hadoop的或多或少都听说过机架感知策略,无论是balancer还是jobtracker分配作业、数据副本放置策略都会用到机架感知。那什么叫机架感知?

首先故名思意机架感知就是感知机架,谁感知?就是hadoop系统嘛,更确切地说是hadoop能在系统内部建立一套服务器和机架的位置拓扑图,并且能识别系统节点的拓扑位置,知道了这些,才能做副本放置策略、作业本地化等更高层的设计。

难道说hadoop系统能自动感应集群或者机房内部的网路拓扑结构?想想看,各个公司的机房拓扑或者网络结构都不一样,采用的设备类型也不相同,hadoop真的那么吊能感受到?显然不能!hadoop系统想获得这个网络拓扑结构,需要系统管理员的帮助。

试想一下,hadoop能构建一幅网络拓扑图,实际的网络拓扑图又千变万化,管理员该怎么弄?所以这时候hadoop有必要设计一抽象的拓扑图结构,管理员需要让实际的网络拓扑结构尽量地与之适配。

Namenode的大管家FSNamesystem有两个重要的成员:

  NetworkTopology clusterMap = new NetworkTopology();
  private DNSToSwitchMapping dnsToSwitchMapping;
  
这两个东西就负责构建了机架及机架感知。

首先说拓扑逻辑类  NetworkTopology clusterMap = new NetworkTopology();这个NetworkTopology构造函数就搞了一把锁就完事了:

  public NetworkTopology() {
    netlock = new ReentrantReadWriteLock();
  }
从构造函数里似乎看不出来啥东西。我看查看这个clusterMap对象,会发现很多地方调用它的add、remove等操作,也许从这里可以看清楚这个网络拓扑类的面貌:

  public void add(Node node) {
    if (node==null) return;
    if( node instanceof InnerNode ) {
      throw new IllegalArgumentException(
        "Not allow to add an inner node: "+NodeBase.getPath(node));
    }
    netlock.writeLock().lock();
    try {
      Node rack = getNode(node.getNetworkLocation());
      if (rack != null && !(rack instanceof InnerNode)) {
        throw new IllegalArgumentException("Unexpected data node " 
                                           + node.toString() 
                                           + " at an illegal network location");
      }
      if (clusterMap.add(node)) {
        LOG.info("Adding a new node: "+NodeBase.getPath(node));
        if (rack == null) {
          numOfRacks++;
        }
      }
      LOG.debug("NetworkTopology became:\n" + this.toString());
    } finally {
      netlock.writeLock().unlock();
    }
  }
首先传递进来的必须是个实现了Node对象,而Node实际上是一个接口类,通过Ctrl+t可以看出我们熟悉的datanodeInfo也是实现了这个接口,另外还有一个NetworkTopology的内部类InnerNode也实现了这个接口,啥是InnerNode先不管,接着往下看。

上面的add方法先进行简单地合法性判断,然后拿到NetworkTopology构建方法里的那把锁,add方法在注册datanode的时候调用,当时传进来的对象是DatanodeDescriptor,注意这一句:

Node rack = getNode(node.getNetworkLocation());
getWorkLocation,怎么得到网络位置?让datanode描述符获得网络位置,datanodeDescriptor继承自DatanodeInfo对象,返回的就是一个string类型的location,初始化的时候直接写死赋值/default-rack,如果中间没有重新set,那么返回的应该是/default-rack,但是datanodeInfo有个set方法,这是注册的时候,想办法给它确定的,这里留下问题1,继续往下:

上面得到一个rack的Node对象,下面又开始调用clusterMap的add方法,注意这个clusterMap是这样的:

InnerNode clusterMap = new InnerNode(InnerNode.ROOT);
就是说这clusterMap是一个InnerNode对象,似乎代表了一个根,这时候继续再看InnerNode,可以看到这个类

  private class InnerNode extends NodeBase {
    private ArrayList children=new ArrayList();
    private int numOfLeaves;
其实是一棵树,每个节点下面都可以挂一批孩子,由于还是是node节点,因此从node的接口实现发现,除了datanodedescripter之外就是innernode,所以说这是一棵树。

现在有了根节点,尝试将注册进来的datanode安排进去,称为add,在整个add过程中,都会频繁出现getNetworkLocation方法,整个东西对理解整段代码阻碍太大了,不得不搞清楚问题1.

问题又回到namenode处理注册的datanode这里来:

  private void resolveNetworkLocation (DatanodeDescriptor node) {
    List names = new ArrayList(1);
    if (dnsToSwitchMapping instanceof CachedDNSToSwitchMapping) {
      // get the node's IP address
      names.add(node.getHost());
    } else {
      // get the node's host name
      String hostName = node.getHostName();
      int colon = hostName.indexOf(":");
      hostName = (colon==-1)?hostName:hostName.substring(0,colon);
      names.add(hostName);
    }
    //特别需要注意这里,解析一个节点属于哪个机架,传入的参数可以是机器的ip也可能是机器名,所以这里要特别注意。在编写解析脚本的时候,必须要考虑到这两种情况
//否则,可能有些机器解析不了。
    // resolve its network location
    List rName = dnsToSwitchMapping.resolve(names);
    String networkLocation;
    if (rName == null) {
      LOG.error("The resolve call returned null! Using " + 
          NetworkTopology.DEFAULT_RACK + " for host " + names);
      networkLocation = NetworkTopology.DEFAULT_RACK;
    } else {
      networkLocation = rName.get(0);
    }
    node.setNetworkLocation(networkLocation);
  }
整个方法用到dnsToSwitchMapping对象的resolve方法,关键的东西来了,这个DNSToSwitchMapping接口就完成了拓扑结构的映射,目前只实现了CachedDNSToSwitchMapping这种,从上面的方法可以看出来你把机器IP输入,通过resolve方法直接告诉了你网络位置。ok,进去看看吧,
  public List resolve(List names) {
    // normalize all input names to be in the form of IP addresses
    names = NetUtils.normalizeHostNames(names);

    List  result = new ArrayList(names.size());
    if (names.isEmpty()) {
      return result;
    }

    List uncachedHosts = this.getUncachedHosts(names);

    // Resolve the uncached hosts
    List resolvedHosts = rawMapping.resolve(uncachedHosts);
    this.cacheResolvedHosts(uncachedHosts, resolvedHosts);
    return this.getCachedHosts(names);

  }
先把你传进来的东西统统强保证成ip的形式,然后看看这些ip是不是被cache住了,如果cache住了,直接就能从cache里拿到这个机器的位置。

否则对于没有cache的就调用rawMapping 的resolve方法,这个rawMapping追踪发现是ScriptBasedMapping类的内部类RawScriptBasedMapping的实例,前面rawMapping 实际调用的resolve方法是:

  public List resolve(List names) {
    List  m = new ArrayList(names.size());
    
    if (names.isEmpty()) {
      return m;
    }

    if (scriptName == null) {
      for (int i = 0; i < names.size(); i++) {
        m.add(NetworkTopology.DEFAULT_RACK);
      }
      return m;
    }
    
    String output = runResolveCommand(names);
    if (output != null) {
      StringTokenizer allSwitchInfo = new StringTokenizer(output);
      while (allSwitchInfo.hasMoreTokens()) {
        String switchInfo = allSwitchInfo.nextToken();
        m.add(switchInfo);
      }
      
      if (m.size() != names.size()) {
        // invalid number of entries returned by the script
        LOG.warn("Script " + scriptName + " returned "
            + Integer.toString(m.size()) + " values when "
            + Integer.toString(names.size()) + " were expected.");
        return null;
      }
    } else {
      // an error occurred. return null to signify this.
      // (exn was already logged in runResolveCommand)
      return null;
    }
    
    return m;
  }

这里就比较简单了,看起来是调用一个脚本,然后执行这个脚本,这个脚本具有这样的功能,输入ip地址给它,它给你返回位置,我擦,这不就是让管理员手工配置一个机器和机架的映射关系吗!

this.scriptName = conf.get(SCRIPT_FILENAME_KEY);
static final String SCRIPT_FILENAME_KEY = "topology.script.file.name";
配置文件中key “topology.script.file.name”指定的脚本的位置,这个脚本要有可执行权限,完成上面的功能。

if (scriptName == null) {
      for (int i = 0; i < names.size(); i++) {
        m.add(NetworkTopology.DEFAULT_RACK);
      }
      return m;
    }
    
如果你没有配置这个东西,那么所有的节点都会在同一个机架上,即"/default-rack"。

好了,那么我们看一下这个脚本该怎么配置。














你可能感兴趣的:(【HDFS】hadoop的机架感知策略是啥?)