zoukankan      html  css  js  c++  java
  • Hadoop源码之Configuration

      本文hadoop版本为最新版本2.6。Configuration做为Hadoop的一个基础功能承担着重要的责任,为Yarn、HSFS、MapReduce、NFS、调度器等提供参数的配置、配置文件的分布式传输(实现了Writable接口)等重要功能。

      Hadoop的加载配置文件的功能没有采用Java自己的java.util.Properties,也没有采用Apache Jakarta Commons中的Commons Configuration,而是自己单独实现了一个自己的Configuration类:org.apache.hadoop.conf.Configuration,在hadoop-common-project子工程中。它的实现子类有:HdfsConfiguration、YarnConfiguration、JobConf、NfsConfiguration、FairSchedulerConfiguration等。

      一、Configuration类重要属性讲解

      A、quitemode:boolean类型,配置信息加载过程中,是否处于安静模式,即有一些信息不会被记录,默认是true;

      B、resources:ArrayList<Resource>类型,Resource是Configuration的内部类,有两个属性Object resource和String name;resources是一个对象数组,用于存储有关包含配置信息的对象;

      C、finalParameters:Set<String>类型,所有被声明为final的变量集合,声明为final就表示不能被后续覆盖;

      D、loadDefaults:boolean类型,是否加载默认配置;

      E、REGISTRY:WeakHashMap<Configuration,Object>类型,用于多个对象的相关配置的注册及对它们进行管理,记录了所有的Configuration;

      F、defaultResources:CopyOnWriteArrayList<String>类型,用于存储默认的配置资源名或路径;

      G、properties:java内置的Properties类型,存储所有配置信息,KV值;

      H、overlay:Properties类型,是用户设置的而不是通过对资源解析得到的;

      I、classloader:ClassLoader类型,主要用于加载指定的类或者加载相关资源;

      J、updatingResource:HashMap<String, String[]>类型,存储最近加载或修改的属性;

      K、VAR_PATTERN:静态Pattern类型,用于正则匹配,Pattern.compile("\$\{[^\}\$u0020]+\}"),正则表达式中$、{、}都是保留字,所以需要用""进行转义,“\$\{”用于匹配${key}中的key前面的"${";最后的"\}"用于匹配key后的"}";中间部分"[^\}\$u0020]+"用于匹配属性扩展键,将匹配除了"$"、"}"和空格(u0020指的是空格)以外的所有字符,还有"+"出现至少1次。

      L、MAX_SUBST:静态int类型,默认值是20,MAX_SUBST是设定对带有环境变量的值所能够深入解析的层次数,超出这个最大的层数的值将不能够解析。

      二、Configuration的初始化

      A、静态代码块,用于加载默认的配置资源

     1     //是一个静态初始化块,用于加载默认的配置资源。
     2     static {
     3         //print deprecation warning if hadoop-site.xml is found in classpath
     4         ClassLoader cL = Thread.currentThread().getContextClassLoader();
     5         if (cL == null) {
     6             cL = Configuration.class.getClassLoader();
     7         }
     8         if (cL.getResource("hadoop-site.xml") != null) {
     9             LOG.warn("DEPRECATED: hadoop-site.xml found in the classpath. " +
    10                     "Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, "
    11                     + "mapred-site.xml and hdfs-site.xml to override properties of " +
    12                     "core-default.xml, mapred-default.xml and hdfs-default.xml " +
    13                     "respectively");
    14         }
    15         addDefaultResource("core-default.xml");
    16         addDefaultResource("core-site.xml");
    17     }

      以上代码会在调用构造方法之前执行,会加载core-default.xml和core-site.xml两个文件,Configuration的子类也会同样加载这两个文件。

      B、三个构造方法,Configuration()、Configuration(boolean loadDefaults)、Configuration(Configuration other),第一个会调用第二个参数是true;第二个确定是否加载默认配置;第三个就是将指定的Configuration对象重新复制一份。

      三、加载资源

      可以通过Configuration的addResource()方法或者静态方法addDefaultResource()(设置了loadDefaults标志)来添加资源到Configuration中。但是add之后资源并不会立即被加载,hadoop的Configuration被设计成了“懒加载”,即在需要时才会被加载。在add之后会调用reloadConfiguration()方法清空properties和finalParameters。

      A、addDefaultResource方法,这是一个静态方法。通过这个方法可以添加系统的默认资源。在HDFS中会被用来加载hdfs-default.xml和hdfs-site.xml;在MapReduce中会被用来加载mapred-default.cml和mapred-site.xml,可以在相关的Configuration子类中找到相应地静态代码块。 

     1     /**
     2      * Add a default resource. Resources are loaded in the order of the resources
     3      * added.
     4      *
     5      * @param name file name. File should be present in the classpath.
     6      */
     7     public static synchronized void addDefaultResource(String name) {
     8         if (!defaultResources.contains(name)) {
     9             defaultResources.add(name);
    10             for (Configuration conf : REGISTRY.keySet()) {
    11                 if (conf.loadDefaults) {
    12                     conf.reloadConfiguration();
    13                 }
    14             }
    15         }
    16     }

      此方法通过遍历REGISTRY中得元素并在元素(Configuration对象)上调用reloadConfiguration方法,就会触发资源的重新加载。

      B、addResource方法,该方法有6种形式:

      public void addResource(Path file)  

      public void addResource(String name)

      public void addResource(URL url)

      public void addResource(InputStream in)

      public void addResource(InputStream in, String name)

      public void addResource(Configuration conf)

      也就是可以add的形式:可以是一个输入流、HDFS文件路径、WEB URL、CLASSPATH资源、以及Configuration对象。这些方法都会将参数封装成Resource对象后,传递给addResourceObject方法并调用该方法。在addResourceObject方法中会将Resource对象加入resources中并调用reloadConfiguration方法。代码如下:

    1     public synchronized void reloadConfiguration() {
    2         properties = null;                            // trigger reload
    3         finalParameters.clear();                      // clear site-limits
    4     }
    5 
    6     private synchronized void addResourceObject(Resource resource) {
    7         resources.add(resource);                      // add to resources
    8         reloadConfiguration();
    9     }

      四、get*取值

      A、get*方法,get*方法一般有两个参数,一个是需要获取属性的名字,另外一个是默认值,以便找不到值时就返回默认值。这些方法都会先通过getTrimmed(name)去掉两端的空格,然后调用get(String name)方法取值。get方法中经过处理过期键之后会调用substituteVars消除属性扩展情况。在调用substituteVars之前会先调用getProps方法,这个方法在发现properties为null时会通过loadResources加载配置资源。

     1 protected synchronized Properties getProps() {
     2         if (properties == null) {
     3             properties = new Properties();
     4             HashMap<String, String[]> backup =
     5                     new HashMap<String, String[]>(updatingResource);
     6             loadResources(properties, resources, quietmode);
     7             if (overlay != null) {
     8                 properties.putAll(overlay);
     9                 for (Map.Entry<Object, Object> item : overlay.entrySet()) {
    10                     String key = (String) item.getKey();
    11                     updatingResource.put(key, backup.get(key));
    12                 }
    13             }
    14         }
    15         return properties;
    16     }

      loadResources相关调用代码如下:

      1 private void loadResources(Properties properties,
      2                                ArrayList<Resource> resources,
      3                                boolean quiet) {
      4         if (loadDefaults) { //加载默认配置资源
      5             for (String resource : defaultResources) {
      6                 loadResource(properties, new Resource(resource), quiet);
      7             }
      8 
      9             //support the hadoop-site.xml as a deprecated case
     10             if (getResource("hadoop-site.xml") != null) {
     11                 loadResource(properties, new Resource("hadoop-site.xml"), quiet);
     12             }
     13         }
     14 
     15         for (int i = 0; i < resources.size(); i++) {    //其他配置资源
     16             Resource ret = loadResource(properties, resources.get(i), quiet);
     17             if (ret != null) {
     18                 resources.set(i, ret);
     19             }
     20         }
     21     }
     22 
     23     private Resource loadResource(Properties properties, Resource wrapper, boolean quiet) {
     24         String name = UNKNOWN_RESOURCE;
     25         try {
     26             Object resource = wrapper.getResource();
     27             name = wrapper.getName();
     28             //得到用于创建DOM解析器的工厂
     29             DocumentBuilderFactory docBuilderFactory
     30                     = DocumentBuilderFactory.newInstance();
     31             //ignore all comments inside the xml file忽略XML中得注释
     32             docBuilderFactory.setIgnoringComments(true);
     33 
     34             //allow includes in the xml file提供对XML命名空间的支持
     35             docBuilderFactory.setNamespaceAware(true);
     36             try {
     37                 //设置XInclude处理状态为true,即允许XInclude机制
     38                 docBuilderFactory.setXIncludeAware(true);
     39             } catch (UnsupportedOperationException e) {
     40                 LOG.error("Failed to set setXIncludeAware(true) for parser "
     41                                 + docBuilderFactory
     42                                 + ":" + e,
     43                         e);
     44             }
     45             //获取解析XML的DocumentBuilder对象
     46             DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
     47             Document doc = null;
     48             Element root = null;
     49             boolean returnCachedProperties = false;
     50             //根据不同资源,做预处理并调用相应刑事的DocumentBuilder.parse
     51             if (resource instanceof URL) {                  // an URL resource
     52                 doc = parse(builder, (URL) resource);
     53             } else if (resource instanceof String) {        // a CLASSPATH resource
     54                 URL url = getResource((String) resource);
     55                 doc = parse(builder, url);
     56             } else if (resource instanceof Path) {          // a file resource
     57                 // Can't use FileSystem API or we get an infinite loop
     58                 // since FileSystem uses Configuration API.  Use java.io.File instead.
     59                 File file = new File(((Path) resource).toUri().getPath())
     60                         .getAbsoluteFile();
     61                 if (file.exists()) {
     62                     if (!quiet) {
     63                         LOG.debug("parsing File " + file);
     64                     }
     65                     doc = parse(builder, new BufferedInputStream(
     66                             new FileInputStream(file)), ((Path) resource).toString());
     67                 }
     68             } else if (resource instanceof InputStream) {
     69                 doc = parse(builder, (InputStream) resource, null);
     70                 returnCachedProperties = true;
     71             } else if (resource instanceof Properties) {
     72                 overlay(properties, (Properties) resource);
     73             } else if (resource instanceof Element) {
     74                 root = (Element) resource;
     75             }
     76 
     77             if (root == null) {
     78                 if (doc == null) {
     79                     if (quiet) {
     80                         return null;
     81                     }
     82                     throw new RuntimeException(resource + " not found");
     83                 }
     84                 root = doc.getDocumentElement();
     85             }
     86             Properties toAddTo = properties;
     87             if (returnCachedProperties) {
     88                 toAddTo = new Properties();
     89             }
     90             //根节点应该是configuration
     91             if (!"configuration".equals(root.getTagName()))
     92                 LOG.fatal("bad conf file: top-level element not <configuration>");
     93             //获取根节点的所有子节点
     94             NodeList props = root.getChildNodes();
     95             DeprecationContext deprecations = deprecationContext.get();
     96             for (int i = 0; i < props.getLength(); i++) {
     97                 Node propNode = props.item(i);
     98                 if (!(propNode instanceof Element))
     99                     continue;   //如果子节点不是Element,则忽略
    100                 Element prop = (Element) propNode;
    101                 if ("configuration".equals(prop.getTagName())) {
    102                 //如果子节点是configuration,递归调用loadResource进行处理,这意味着configuration的子节点可以是configuration
    103                     loadResource(toAddTo, new Resource(prop, name), quiet);
    104                     continue;
    105                 }
    106                 //子节点是property
    107                 if (!"property".equals(prop.getTagName()))
    108                     LOG.warn("bad conf file: element not <property>");
    109                 NodeList fields = prop.getChildNodes();
    110                 String attr = null;
    111                 String value = null;
    112                 boolean finalParameter = false;
    113                 LinkedList<String> source = new LinkedList<String>();
    114                 //查找name、value、final的值
    115                 for (int j = 0; j < fields.getLength(); j++) {
    116                     Node fieldNode = fields.item(j);
    117                     if (!(fieldNode instanceof Element))
    118                         continue;
    119                     Element field = (Element) fieldNode;
    120                     if ("name".equals(field.getTagName()) && field.hasChildNodes())
    121                         attr = StringInterner.weakIntern(
    122                                 ((Text) field.getFirstChild()).getData().trim());
    123                     if ("value".equals(field.getTagName()) && field.hasChildNodes())
    124                         value = StringInterner.weakIntern(
    125                                 ((Text) field.getFirstChild()).getData());
    126                     if ("final".equals(field.getTagName()) && field.hasChildNodes())
    127                         finalParameter = "true".equals(((Text) field.getFirstChild()).getData());
    128                     if ("source".equals(field.getTagName()) && field.hasChildNodes())
    129                         source.add(StringInterner.weakIntern(
    130                                 ((Text) field.getFirstChild()).getData()));
    131                 }
    132                 source.add(name);
    133 
    134                 // Ignore this parameter if it has already been marked as 'final'
    135                 if (attr != null) {
    136                     if (deprecations.getDeprecatedKeyMap().containsKey(attr)) {
    137                         DeprecatedKeyInfo keyInfo =
    138                                 deprecations.getDeprecatedKeyMap().get(attr);
    139                         keyInfo.clearAccessed();
    140                         for (String key : keyInfo.newKeys) {
    141                             // update new keys with deprecated key's value
    142                             loadProperty(toAddTo, name, key, value, finalParameter,
    143                                     source.toArray(new String[source.size()]));
    144                         }
    145                     } else {
    146                         loadProperty(toAddTo, name, attr, value, finalParameter,
    147                                 source.toArray(new String[source.size()]));
    148                     }
    149                 }
    150             }
    151 
    152             if (returnCachedProperties) {
    153                 overlay(properties, toAddTo);
    154                 return new Resource(toAddTo, name);
    155             }
    156             return null;
    157         } catch (IOException e) {
    158             LOG.fatal("error parsing conf " + name, e);
    159             throw new RuntimeException(e);
    160         } catch (DOMException e) {
    161             LOG.fatal("error parsing conf " + name, e);
    162             throw new RuntimeException(e);
    163         } catch (SAXException e) {
    164             LOG.fatal("error parsing conf " + name, e);
    165             throw new RuntimeException(e);
    166         } catch (ParserConfigurationException e) {
    167             LOG.fatal("error parsing conf " + name, e);
    168             throw new RuntimeException(e);
    169         }
    170     }
    View Code

      如上,如果允许(loadDefaults==true)加载默认资源则会优先加载defaultResources中得资源,如果CLASSPATH下还有hadoop-site.xml文件也会加载;最后将指定的资源进行加载,因为有顺序,所以有同名的话会被覆盖,除非是final类型的。

      通过以上getProps就会获得所有配置信息了,调用其getProperty方法就可以获取需要属性的值了。再传递给substituteVars进行属性扩展,代码如下:

     1 //是配合正则表达式对象对含有环境变量的参数值进行解析的方法
     2     private String substituteVars(String expr) {
     3         if (expr == null) {
     4             return null;
     5         }
     6         Matcher match = VAR_PATTERN.matcher("");
     7         String eval = expr;
     8         //循环,最多做MAX_SUBST次属性扩展
     9         for (int s = 0; s < MAX_SUBST; s++) {
    10             match.reset(eval);
    11             if (!match.find()) {
    12                 return eval;    //什么都没找到,返回
    13             }
    14             String var = match.group();
    15             var = var.substring(2, var.length() - 1); // remove ${ .. }获得属性扩展的键
    16             String val = null;
    17             try {
    18                 //俺看java虚拟机的系统属性有没有var对应的val,这一步保证了优先使用java的系统属性
    19                 val = System.getProperty(var);
    20             } catch (SecurityException se) {
    21                 LOG.warn("Unexpected SecurityException in Configuration", se);
    22             }
    23             if (val == null) {
    24                 val = getRaw(var);  //然后是Configuration对象中得配置属性
    25             }
    26             if (val == null) {
    27                 //属性扩展中得var没有绑定,不做扩展,返回
    28                 return eval; // return literal ${var}: var is unbound
    29             }
    30             // substitute替换${ ... },完成属性扩展
    31             eval = eval.substring(0, match.start()) + val + eval.substring(match.end());
    32         }
    33         //属性扩展次数太多,抛出异常
    34         throw new IllegalStateException("Variable substitution depth too large: "
    35                 + MAX_SUBST + " " + expr);
    36     }

      这里会限制扩展次数,优先考虑配置的系统属性,然后是Configuration中配置的属性。java系统属性,可以通过-DXXX=YYY的方式在jvm或者启动命令中指定。

      这样set*获取到string类型的值了,然后可以根据返回类型进行处理。

      五、set*设置配置项,set相对于get则要简单一些,set*方法最终会调用set(String name, String value, String source)方法,source方法用来说明configuration的来源,一般设置为null,这个方法会调用properties和overlay的setProperty()方法,保存传入的键值对,同时也会更新updatingResource。

      在编写mapreduce时,可能需要各个task共享一些数据,可以通过Configuration的set*方法来配置,并在mapper或者reducer中setup方法中的context获取。

      总之来说,一、创建对象,会加载默认资源(前提是loadResource=true);二、add资源(可选,没有这步就是hadoop默认的资源了),会清楚原来的数据,但不会立即加载资源;三、get*方法,会触发资源的加载(getProps),处理属性扩展等,返回属性对应的值;四、set*设置自己的参数。

       

    参考:

      1、蔡斌 陈湘萍 《Hadoop技术内幕---深入将诶西Hadoop Common和HDFS结构设计与实现原理》

      2、一些网络资源

  • 相关阅读:
    DP——背包问题(三)
    堆——练习题
    DP——背包问题(二)
    二叉树的后序遍历(暴力版) 小白菜oj 1034
    树状数组2模板 Luogu 3368
    树状数组1模板 Luogu 3374
    DP——最长上升子序列(n^2与n log n)
    线段树(区间修改)模板题 Luogu 2357 守墓人
    c语言学习摘录
    python 学习摘录
  • 原文地址:https://www.cnblogs.com/lxf20061900/p/4189727.html
Copyright © 2011-2022 走看看