NameNode对域名/IP的解析——DNSToSwitchMapping

来源:互联网 发布:sql union 多列 编辑:程序博客网 时间:2024/05/01 08:26

          前面介绍Networktopology结构的时候就说过,NameNode把注册的DataNode节点按照他们的ip地址存储到一个树状网络拓扑图中(对应Networktopology的一个实例),然后NameNode调用ReplicationTargetChooser来为每一个数据块副本选择合适的存储节点。那么,NameNode是如何把一个数据节点按照它的ip解析到对应的树状网络拓扑图中的一个叶子节点呢?

       实际上,这个过程很简单,NameNode把这个NameNode节点所在主机的ip地址按照用户设计的规则转换成一个对应的路径格式(如:/*/*/*/*),这个路径就对应了树状网络拓扑图中的一个非叶子节点,NameNode就把这个数据节点存放到这个非叶子节点下面,作为它的叶子节点。现在关键的问题就是NameNode如何把一个ip地址解析成一个路径的形式。刚才说了,NameNode是按照用户设计的转换规则来的,它自己并不知道如何转换,也就是说这个转换是交由用户自己来实现的。

        HDFS将ip地址转换成路径的操作交给了用户自己来实现,即用户通过自己实现接口DNSToSwitchMapping,然后在配置文件中指明实现的这个类,NameNode就能够自动的调用用户的解析实现了。哦,对了,这个对应的配置项是:topology.node.switch.mapping.impl。

        现在我想说一下,HDFS为什么要把ip地址的解析交给用户自己来实现?这是因为HDFS不得不把这个过程交给用户来实现,只有集群管理员即用户才知道哪些ip地址对应的机器被放在同一个机架下面,哪些机架在同一个路由器下面,说白了就是只有集群管理员才知道机器之间的网络距离。好了,现在就来具体看看与DNSToSwitchMapping相关的实现。


       HDFS考虑到性能问题,一方面把DNSToSwitchMapping接口定义成一个批量解析任务,另一方面自己还定义了一个缓存类CacheDNSToSwitchMapping来保存解析的结果。同时,HDFS自己也定义了一个DNSToSwitchMapping的实现RawScriptBasedMapping,这个实现类被设计成了一个插件集成类,它通过执行shell脚本语言调用第三方的实现来获取一批ip地址的解析结果,这实际上还是将真正的解析过程交给了用户。在默认的情况下,NameNode使用ScriptBasedMapping来解析ip地址。现在来好好的分析一下这个RawScriptBasedMapping实现类

      RawScriptBasedMapping类主要包含三个参数

scriptName:调用第三方ip解析实现的脚本命令;

maxArgs第三方ip解析实现一次调用最多能解析多少个ip;

conf:通过配置文件获取scriptNamemaxArgs

       RawScriptBasedMapping根据NameNode传过来的ip解析任务分批调用第三方ip解析器来执行这些ip的解析,每一批不能超过maxArgs个ip地址。调用的过程如下:

private String runResolveCommand(List<String> args) {    int loopCount = 0;    if (args.size() == 0) {      return null;    }    StringBuffer allOutput = new StringBuffer();    int numProcessed = 0;    if (maxArgs < MIN_ALLOWABLE_ARGS) {      LOG.warn("Invalid value " + Integer.toString(maxArgs) + " for " + SCRIPT_ARG_COUNT_KEY + "; must be >= " + Integer.toString(MIN_ALLOWABLE_ARGS));      return null;    }        while (numProcessed != args.size()) {      int start = maxArgs * loopCount;      List <String> cmdList = new ArrayList<String>();      cmdList.add(scriptName);      for (numProcessed = start; numProcessed < (start + maxArgs) && numProcessed < args.size(); numProcessed++) {        cmdList.add(args.get(numProcessed));      }      File dir = null;      String userDir;      if ((userDir = System.getProperty("user.dir")) != null) {        dir = new File(userDir);      }      ShellCommandExecutor s = new ShellCommandExecutor(cmdList.toArray(new String[0]), dir);      try {        s.execute();        allOutput.append(s.getOutput() + " ");      } catch (Exception e) {        LOG.warn(StringUtils.stringifyException(e));        return null;      }      loopCount++;    }        return allOutput.toString();  }
     现在我将举一个完整的例子,即自己实现一个第三方的ip解析插件,来详细的阐述整个实现与配置过程:

1).自定义一个ip解析器

        利用C语言写一个简单的程序,对每一个传入的ip地址,都输出对应的一个路径,然后编译连接生成一个可执行文件:ipresolve

#include<stdio.h>int main(int argc, char ** argv){        if(argc>1){        int i;        for(i=1; i<argc; ++i) printf("/rack/rakc/rakc%d\n",i);    }        return 0;}

2).在HDFS的配置文件core-site.xml中配置这个第三方插件

<property>  <name>topology.script.file.name</name>  <value>/home/**/ipresolve</value>  <description>可执行文件ipresolve的绝对路径</description></property><property>  <name>topology.script.number.args</name>  <value>10</value>  <description></description></property>

3).测试

public static void main(String[] args){      Configuration conf = new Configuration();      ScriptBasedMapping sbp = new ScriptBasedMapping();      sbp.setConf(conf);            List<String> ips = new ArrayList<String>();      ips.add("127.0.0.1");      ips.add("127.0.0.2");      ips.add("127.0.0.3");      ips.add("127.0.0.4");            List<String> result = sbp.resolve(ips);      for(int i=0; i<result.size(); ++i){          System.out.println(result.get(i));      }  }

4).测试输出

      当然,对用户来说有两种方式来实现用户自定义的ip解析器,一中是直接实现DNSToSwitchMapping接口,并将这个实现类配置到配置文件的项;另一种就是上面的的插件方式。就性能而言,第一种方式为佳,就可扩展行而言,第二种方式为佳,不过,这最后还是取决于用户自己的应用场景。