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

前面介绍Networktopology结构的时候就说过(见 http://www.linuxidc.com/Linux/2012-01/50863.htm ),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相关的实现。

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

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

      RawScriptBasedMapping类主要包含三个参数

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

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

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

conf:通过配置文件获取scriptNamemaxArgs

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

 
  1. private String runResolveCommand(List<String> args) {  
  2.     int loopCount = 0;  
  3.     if (args.size() == 0) {  
  4.       return null;  
  5.     }  
  6.     StringBuffer allOutput = new StringBuffer();  
  7.     int numProcessed = 0;  
  8.     if (maxArgs < MIN_ALLOWABLE_ARGS) {  
  9.       LOG.warn("Invalid value " + Integer.toString(maxArgs) + " for " + SCRIPT_ARG_COUNT_KEY + "; must be >= " + Integer.toString(MIN_ALLOWABLE_ARGS));  
  10.       return null;  
  11.     }  
  12.       
  13.     while (numProcessed != args.size()) {  
  14.       int start = maxArgs * loopCount;  
  15.       List <String> cmdList = new ArrayList<String>();  
  16.       cmdList.add(scriptName);  
  17.       for (numProcessed = start; numProcessed < (start + maxArgs) && numProcessed < args.size(); numProcessed++) {  
  18.         cmdList.add(args.get(numProcessed));  
  19.       }  
  20.       File dir = null;  
  21.       String userDir;  
  22.       if ((userDir = System.getProperty("user.dir")) != null) {  
  23.         dir = new File(userDir);  
  24.       }  
  25.       ShellCommandExecutor s = new ShellCommandExecutor(cmdList.toArray(new String[0]), dir);  
  26.       try {  
  27.         s.execute();  
  28.         allOutput.append(s.getOutput() + " ");  
  29.       } catch (Exception e) {  
  30.         LOG.warn(StringUtils.stringifyException(e));  
  31.         return null;  
  32.       }  
  33.       loopCount++;  
  34.     }  
  35.       
  36.     return allOutput.toString();  
  37.   }  
     现在我将举一个完整的例子,即自己实现一个第三方的ip解析插件,来详细的阐述整个实现与配置过程:

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

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

 
  1. #include<stdio.h>   
  2.   
  3. int main(int argc, char ** argv){  
  4.       
  5.     if(argc>1){  
  6.         int i;  
  7.         for(i=1; i<argc; ++i) printf("/rack/rakc/rakc%d\n",i);  
  8.     }  
  9.       
  10.     return 0;  
  11. }  

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

  1. <property>  
  2.   <name>topology.script.file.name</name>  
  3.   <value>/home/**/ipresolve</value>  
  4.   <description>可执行文件ipresolve的绝对路径</description>  
  5. </property>  
  6.   
  7. <property>  
  8.   <name>topology.script.number.args</name>  
  9.   <value>10</value>  
  10.   <description></description>  
  11. </property>  

3).测试

  1. public static void main(String[] args){  
  2.       Configuration conf = new Configuration();  
  3.       ScriptBasedMapping sbp = new ScriptBasedMapping();  
  4.       sbp.setConf(conf);  
  5.         
  6.       List<String> ips = new ArrayList<String>();  
  7.       ips.add("127.0.0.1");  
  8.       ips.add("127.0.0.2");  
  9.       ips.add("127.0.0.3");  
  10.       ips.add("127.0.0.4");  
  11.         
  12.       List<String> result = sbp.resolve(ips);  
  13.       for(int i=0; i<result.size(); ++i){  
  14.           System.out.println(result.get(i));  
  15.       }  
  16.   }  

4).测试输出

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

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

相关推荐