5.HDFS核心_Configuration

一.Configuration介绍

  • Configuration是Hadoop的公共类,所以被放在了hadoop-common-2.7.4.jar下:org.apache.hadoop.conf.Configruration。该类是Job的配置信息类,配置信息的传递必须通过Configuration。因为通过Configuration可以实现在多个mapper和多个reducer任务间共享信息。
  • Configuration实现了Iterable和Writable两个接口。因此它具有迭代功能,迭代Configuration对象中所有name-value键值对,加载到内存。实现Writable是为了实现Hadoop框架要求的序列化,可以将内存中的name-value序列化到硬盘。

二.Configuration分析

Configuration是一个配置信息类,它包含配置信息的加载,获取配置信息和加载配置信息等。类加载的过程是,先加载该类的静态代码块,再加载静态变量,然后才是构造方法。所以,我在分析的时候,先看静态代码块,然后再看构造方法,最后根据该类的主要功能去分析。

1.加载静态代码块

  static{
    //print deprecation warning if hadoop-site.xml is found in classpath
    ClassLoader cL = Thread.currentThread().getContextClassLoader();
    if (cL == null) {
      cL = Configuration.class.getClassLoader();
    }
    if(cL.getResource("hadoop-site.xml")!=null) {
      LOG.warn("DEPRECATED: hadoop-site.xml found in the classpath. " +
          "Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, "
          + "mapred-site.xml and hdfs-site.xml to override properties of " +
          "core-default.xml, mapred-default.xml and hdfs-default.xml " +
          "respectively");
    }
    addDefaultResource("core-default.xml");
    addDefaultResource("core-site.xml");
  }

在静态代码块中,我们看到Configuration静态代码块加载的时候,加载了默认资源"core-default.xml"和"core-site.xml"。我们看addDefaultResource(...)方法。

  public static synchronized void addDefaultResource(String name) {
    if(!defaultResources.contains(name)) {
      defaultResources.add(name);
      for(Configuration conf : REGISTRY.keySet()) {
        if(conf.loadDefaults) {
          conf.reloadConfiguration();
        }
      }
    }
  }

这个方法中将配置文件名添加到集合defaultResources中。其中loadDefaults是一个flag,标示是否要加载默认配置文件(默认true)。然后将集合REGISTORY中的Configuration对象(只要该对象加载默认配置)进行reloadConfiguration(),即:重新加载配置文件。这里出现了一个集合REGISTORY,我们下面遇到再说。先看reloadConfiguration()方法。

  public synchronized void reloadConfiguration() {
    properties = null;                            // trigger reload
    finalParameters.clear();                      // clear site-limits
  }

这个方法实际上是将Configuration的properties属性清空,同时,将集合finalParameters(该集合里存放了final修饰的变量)清空。

总之,加载静态代码块的结果是:将"core-default.xml"和"core-site.xml"文件名添加到defaultResources然后清空了properties属性和finalParameters集合。

2.构造方法

  /** A new configuration. */
  public Configuration() {
    this(true);
  }

  /** A new configuration where the behavior of reading from the default 
   * resources can be turned off.
   * 
   * If the parameter {@code loadDefaults} is false, the new instance
   * will not load resources from the default files. 
   * @param loadDefaults specifies whether to load from the default files
   */
  public Configuration(boolean loadDefaults) {
    this.loadDefaults = loadDefaults;
    updatingResource = new ConcurrentHashMap();
    synchronized(Configuration.class) {
      REGISTRY.put(this, null);
    }
  }
  
  /** 
   * 克隆其他configuration的配置
   */
  @SuppressWarnings("unchecked")
  public Configuration(Configuration other) {
   this.resources = (ArrayList) other.resources.clone();
   synchronized(other) {
     if (other.properties != null) {
       this.properties = (Properties)other.properties.clone();
     }

     if (other.overlay!=null) {
       this.overlay = (Properties)other.overlay.clone();
     }

     this.updatingResource = new ConcurrentHashMap(
         other.updatingResource);
     this.finalParameters = Collections.newSetFromMap(
         new ConcurrentHashMap());
     this.finalParameters.addAll(other.finalParameters);
   }
   
    synchronized(Configuration.class) {
      REGISTRY.put(this, null);
    }
    this.classLoader = other.classLoader;
    this.loadDefaults = other.loadDefaults;
    setQuietMode(other.getQuietMode());
  }

我们可以看到前两个构造方法是典型的折叠式构造方式,第一个无参构造中调用了第二个构造方法。在第二个构造方法中设置了loadDefaults为true,将Configuration实例本身放到REGISTRY集合中注册。第三个构造方法是,克隆其他Configuration的配置信息。

3. 获取配置信息

我们在outline中看到了很多getType(...)方法。我们来分析一下运行过程,以getInt为例:

  public int getInt(String name, int defaultValue) {
    String valueString = getTrimmed(name);
    if (valueString == null)
      return defaultValue;
    String hexString = getHexDigits(valueString);
    if (hexString != null) {
      return Integer.parseInt(hexString, 16);
    }
    return Integer.parseInt(valueString);
  }

  public String getTrimmed(String name) {
    String value = get(name);
    
    if (null == value) {
      return null;
    } else {
      return value.trim();
    }
  }

  public String get(String name) {
    String[] names = handleDeprecation(deprecationContext.get(), name);
    String result = null;
    for(String n : names) {
      result = substituteVars(getProps().getProperty(n));
    }
    return result;
  }

  protected synchronized Properties getProps() {
    if (properties == null) {
      properties = new Properties();
      Map backup =
          new ConcurrentHashMap(updatingResource);
      loadResources(properties, resources, quietmode);

      if (overlay != null) {
        properties.putAll(overlay);
        for (Map.Entry item: overlay.entrySet()) {
          String key = (String)item.getKey();
          String[] source = backup.get(key);
          if(source != null) {
            updatingResource.put(key, source);
          }
        }
      }
    }
    return properties;
  }

  private void loadResources(Properties properties,
                             ArrayList resources,
                             boolean quiet) {
    if(loadDefaults) {
      for (String resource : defaultResources) {
        loadResource(properties, new Resource(resource), quiet);
      }
    
      //support the hadoop-site.xml as a deprecated case
      if(getResource("hadoop-site.xml")!=null) {
        loadResource(properties, new Resource("hadoop-site.xml"), quiet);
      }
    }
    
    for (int i = 0; i < resources.size(); i++) {
      Resource ret = loadResource(properties, resources.get(i), quiet);
      if (ret != null) {
        resources.set(i, ret);
      }
    }
  }

我们看到getInt --> getTrimmed --> get --> getProps,这是所有getType()方法调用要走的路径,最终调用到getProps()方法。在该方法中,如果properties为空,我们就要执行loadResources(...)加载资源,否则直接返回properties属性即可。

接下来我们看loadResources(...)方法。在该方法中,我们上文提到的loadDefaults就起作用了。如果loadDefaults=true,则加载集合defaultResources中的默认配置文件和"hadoop-site.xml"配置文件。否则,只加载集合resources中的配置文件。

  • private ArrayList resources = new ArrayList();
  • private static final CopyOnWriteArrayList defaultResources = new CopyOnWriteArrayList();

4.加载资源的方式

从上面可以看到一个是default resource配置文件的list,另一个是configuration resources配置文件的list。我们要看看Configuration类是如何加载配置文件的。首先,我们在静态代码块中看到了addDefaultResources(...)方法,另外还有addResources(...)的多个重载方法。最终addResources()方法调用的是addResourceObject(...)方法,所以我们看addResourceObject(...)和addDefaultResources(...)两个方法就可以。

 public void addResource(URL url) {
    addResourceObject(new Resource(url));
  }

 private synchronized void addResourceObject(Resource resource) {
    resources.add(resource);                      // add to resources
    reloadConfiguration();
  }

  public synchronized void reloadConfiguration() {
    properties = null;                            // trigger reload
    finalParameters.clear();                      // clear site-limits
  }

  public static synchronized void addDefaultResource(String name) {
    if(!defaultResources.contains(name)) {
      defaultResources.add(name);
      for(Configuration conf : REGISTRY.keySet()) {
        if(conf.loadDefaults) {
          conf.reloadConfiguration();
        }
      }
    }
  }

加载资源的过程,就是分别调用addDefaultResources(...)方法将默认配置文件名添加到defaultResources集合、调用addResourceObject(...)方法将配置文件加如到resources集合中。同时,清空finalParameters并重置properties=null。

5.设置配置信息

通过outline可以看到很多setType(...)方法,我们以setInt()为例:

  public void setInt(String name, int value) {
    set(name, Integer.toString(value));
  }

  public void set(String name, String value) {
    set(name, value, null);
  }

  public void set(String name, String value, String source) {
    Preconditions.checkArgument(
        name != null,
        "Property name must not be null");
    Preconditions.checkArgument(
        value != null,
        "The value of property " + name + " must not be null");
    name = name.trim();
    DeprecationContext deprecations = deprecationContext.get();
    if (deprecations.getDeprecatedKeyMap().isEmpty()) {
      getProps();
    }
    getOverlay().setProperty(name, value);
    getProps().setProperty(name, value);
    String newSource = (source == null ? "programatically" : source);

    if (!isDeprecated(name)) {
      updatingResource.put(name, new String[] {newSource});
      String[] altNames = getAlternativeNames(name);
      if(altNames != null) {
        for(String n: altNames) {
          if(!n.equals(name)) {
            getOverlay().setProperty(n, value);
            getProps().setProperty(n, value);
            updatingResource.put(n, new String[] {newSource});
          }
        }
      }
    }
    else {
      String[] names = handleDeprecation(deprecationContext.get(), name);
      String altSource = "because " + name + " is deprecated";
      for(String n : names) {
        getOverlay().setProperty(n, value);
        getProps().setProperty(n, value);
        updatingResource.put(n, new String[] {altSource});
      }
    }
  }

setType --> set --> set,最终就是把属性项和值添加到properties变量和overlay变量中。overlay是用来覆盖properties的,因为每次加载配置文件的过程,都要重置properties=null。overlay可以看着properties的一种备份。

你可能感兴趣的:(HDFS)