• 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、一些网络资源

  • 相关阅读:
    Unable To Open Database After ASM Upgrade From Release 11.1 To Release 11.2
    11g Understanding Automatic Diagnostic Repository.
    How to perform Rolling UpgradeDowngrade in 11g ASM
    Oracle 11.2.0.2 Patch 说明
    Pattern Matching Metacharacters For asm_diskstring
    Steps To MigrateMove a Database From NonASM to ASM And ViceVersa
    Upgrading ASM instance from Oracle 10.1 to Oracle 10.2. (Single Instance)
    OCSSD.BIN Process is Running in a NonRAC Environment
    Steps To MigrateMove a Database From NonASM to ASM And ViceVersa
    On RAC, expdp Removes the Service Name [ID 1269319.1]
  • 原文地址:https://www.cnblogs.com/lxf20061900/p/4189727.html
Copyright © 2020-2023  润新知