hadoop中Configuration类剖析

Configuration是hadoop中五大组件的公用类,所以放在了core下,org.apache.hadoop.conf.Configruration。这个类是作业的配置信息类,任何作用的配置信息必须通过Configuration传递,因为通过Configuration可以实现在多个mapper和多个reducer任务之间共享信息。

类图

说明:Configuration实现了Iterable和Writable两个接口,其中实现Iterable是为了迭代,迭代出Configuration对象加载到内存中的所有name-value键值对。实现Writable是为了实现hadoop框架要求的序列化,可以将内存中的name-value序列化到硬盘,关于这两个接口的具体实现的话,我想不用再多说了,应该想的明白。

下面来详细的分析下Configuration的工作原理,包含配置文件的加载,获取配置信息和加载配置信息的原理,以及在使用过程中应该注意的事项。

研究任何一个类首先从构造函数开始,就算是使用的单例,静态工厂得到对象也同样离不开Constructor。

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 HashMap<String, String>();
    synchronized(Configuration.class) {
      REGISTRY.put(this, null);
    }
  }
/**
   * A new configuration with the same settings cloned from another.
   *
   * @param other the configuration from which to clone settings.
   */
  @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 HashMap<String, String>(
          other.updatingResource);
    }
    this.finalParameters = new HashSet<String>(other.finalParameters);
    synchronized (Configuration.class) {
      REGISTRY.put(this, null);
    }
  }

1,Configuration()

2,Configuration(boolean loadDefaults)

3, Configuration(Configuraiont other)

前两个Constructor使用的是典型的重叠构造器模式,也就是默认的无参Constructor会生成一个加载了默认配置文件得Configuration对象,其中Configuration(boolean loadDefaults)中的参数就是为了控制构造出来的对象是加载了默认配置文件还是没有的标识。但是如果要我来设计我不会搞得这么麻烦,直接使用两个静态工厂方法来标识不同性质的对象——getConfigruationWithDefault()和getConfiguration,这样的话开发人员在使用是就可以望文生义,不是很好的方式么?不扯这个了。当loadDefaults为false时,Configuration对象就不会将通过addDefaultResource(String resource)加载的配置文件载入内存。但是会将通过addResource(...)加载的配置文件载入内存。具体是怎么实现的呢?

在Configuration这个Constructor中的this.loadDefaults = loadDefaults就是设置是否加载默认配置文件的flag,我们顺蔓摸瓜,构造了Configuration对象后,接下来会调用getType(String name,Type default)方法得到某个name对应的value值。以getInt为例,看看getInt()的代码

getInt(String name,int defalutVale)

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

方法的第一行代码String valueString = get(name);是关键,所以再来看看get(String name)这个方法

get(String name)

private synchronized Properties getProps() {
    if (properties == null) {
      properties = new Properties();
      loadResources(properties, resources, quietmode);
      if (overlay!= null) {
        properties.putAll(overlay);
        for (Map.Entry<Object,Object> item: overlay.entrySet()) {
          updatingResource.put((String) item.getKey(), UNKNOWN_RESOURCE);
        }
      }
    }
    return properties;
  }

这里就有话可讲了,由getInt --> get --> getProps的路径是任何一次getType方法调用要走的路径,但是在getProps()这个地方就要分道扬镳了,第一次用getType方法在判断了properties == null后会执行loadResources(properties,resources,quietmode)方法。但是在properties不为null的情况下就不会执行后续代码。下面走进loadResources(properties,resources,quietmode)方法一探究竟

loadResources(properties,resources,quietmode)

private void loadResources(Properties properties,
                             ArrayList resources,
                             boolean quiet) {
    if(loadDefaults) {
      for (String resource : defaultResources) {
        loadResource(properties, resource, quiet);
      }

      //support the hadoop-site.xml as a deprecated case
      if(getResource("hadoop-site.xml")!=null) {
        loadResource(properties, "hadoop-site.xml", quiet);
      }
    }

    for (Object resource : resources) {
      loadResource(properties, resource, quiet);
    }
  }

看到了loadDefaults了没有?是不是很开心,在Constructor涉及到的控制默认配置文件加载的loadDefaults终于现身了。defaultResource在loadDefaults为true是才会加载。但是resources中存放的配置文件无论怎么样都会被加载,这里出现了两个存放配置文件的容器defaultResources和resource

/**
   * List of configuration resources.
   */
  private ArrayList<Object> resources = new ArrayList<Object>();
/**
   * List of default Resources. Resources are loaded in the order of the list
   * entries
   */
  private static final CopyOnWriteArrayList<String> defaultResources =
    new CopyOnWriteArrayList<String>();

一个是cofiguration resources的list,一个default Resources的list,那么如何区分是否是default resources呢?别着急,看下面的分析。在Configuration类中有多个加载配置文件的方法 ,addDefaultResource(String name),addResource(String resoruce)及重载方法,addResourceObject(Object resource)。由于addResource(...)系类的方法最终是通过调用addResourceObject来实现的,所以这个就要看addDefaultResource(String name)和addResourceObject(Object resource)的区别了

addDefaultResource(String resource)

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

addResourceObject(Object object)

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

看清楚没有?没看清楚多看一下。addDefaultResource(String name)内部通过defaultResources.add(name)将配置文件的name加入了容器defaultResources容器中,addResourceObject(Object resource)通过resources.add(resource)将配置文件加入了resources容器中。所以这就说明了,所以的默认配置文件是通过addDefaultResource(String name)加载的,也就存放在defaultResources这个容器中的,存放在resources中的配置文件就不能当做是默认的配置文件了。

仔细观察这两个方法的实现,发现reloadConfiguration(),这里面有文章可以做,还是看源码说话吧

reloadConfiguration()

/**
   * Reload configuration from previously added resources.
   *
   * This method will clear all the configuration read from the added
   * resources, and final parameters. This will make the resources to
   * be read again before accessing the values. Values that are added
   * via set methods will overlay values read from the resources.
   */
  public synchronized void reloadConfiguration() {
    properties = null;                            // trigger reload
    finalParameters.clear();                      // clear site-limits
  }

恩,properties=null,fianlParmeters.clear(),这就将内存中存在的name-value都清空了。所以在使用getType方法后又得重新将配置文件载入内存,所以建议在作业运行的过程中不要使用addDefaultResource(String resource)和addResourceObject(Object object),因为这会导致重新加载配置文件到内存。有必要解释下finalParameters这个filed,该feilds也是一个Set容器,主要是用来存储被final修饰的name-value,被fianl修饰后的name-value无法被后续的配置文件覆盖,但是在程序中可以通过set(String name,String value),这里让人不明白,不允许管理员通过配置文件修改的name-value但是可以被用户修改,实则是很奇怪。

关于第三个构造函数,根据参数和具体实现很容易知道是生成一个和传入的configuration对象一样的configuration对象,不说了这里。

现在关于构造Configuration对象时如何控制是否加载的配置文件原理已经清楚了,同时也弄清楚了getType的原理。下面是调用getType方法的时序图

                                               

  构造器,getType原理应该已经清楚了,现在来看下setType方法,setType(String name ,Type value)方法内部都调用了set(String name,String value)方法,这一点和getType(String name,Type defaultValue)与get(String)的关系是相同的。那么现在来思考一个问题:上面说了,在使用addDefaultResources(...)和addResourceObject(...)方法都会清空内存中的name-value键值对,放在配置文件中的name-value可以重新加载到内存中,也就说这些name-value键值对并不会丢失。但是通过setType()设置的值别没有写到配置文件中,他们是存在内存当中。

public void set(String name, String value) {
    getOverlay().setProperty(name, value);
    getProps().setProperty(name, value);
    this.updatingResource.put(name, UNKNOWN_RESOURCE);
  }

getProps返回的是存放有所有的name-value键值对的Properties对象,使用set(String name,String value)方法设置的name-value仅仅是放在了properties对象的内存空间总并没有写入到文件,这样addDefaultResources(...)和addResourceObject(...)时properties被设置为null后,好不容易通过set(String name,String value)加载进来的name-value岂不是丢弃了?

注意在set(String name,String value)中还有一个地方是关键getOverlay().setProperty(name, value),其中getOverlay()方法返回的overlay,该对象的引用类型是Properties。矛盾来了,set(String name,String value)方法将name-value加到了两个Properties对象中,这又是干什么?呀,现在可以肯定的是通过set(String name,String value)方法设置的name-value键值对在字段overlay对象和字段properties都有,在回头来看看getProps()方法

private synchronized Properties getProps() {
    if (properties == null) {
      properties = new Properties();
      loadResources(properties, resources, quietmode);
      if (overlay!= null) {
        properties.putAll(overlay);
        for (Map.Entry<Object,Object> item: overlay.entrySet()) {
          updatingResource.put((String) item.getKey(), UNKNOWN_RESOURCE);
        }
      }
    }
    return properties;
  }

在properties为null的条件下,除了会去加载配置文件中value-name,还会探测一下overlay对象是不是为空,不为空就将overlay对象中的name-value加载到properties中,恩,这一点和reloadConfiguration()不矛盾,因为reloadConfiguration()是将properties对象置为 null,并没有将overlay置为空。可以这样说overlay的作用是将用户设置的name-value保存起来作为properties在内存部分的备份,这样properties中由系统和管理员配置的name-value由配置文件备份,而后期用户载入的name-value则有overlay备份到内存中,properties在configuration对象存活期间不会有信息丢失。

setType和getType方法都可以触发loadResources()方法将name-value加入到properties对象的内存中,但是一旦properties已经存放了配置文件中的name-value键值对,再次调用setType或者是getType方法就不会触发loadResources()的加载动作,除非调用了addDefaultResources(...)和addResourceObject(...)。

Summarize:

1 在作业运行过程中不要使用addDefaultResources(...)和addResourceObject(...)加载资源,因为这会导致properties对象重构一遍,建议此时使用setType(...)

2 Configuration在整个MapReduce中使用得很频繁,JobTraker,TaskTraker进程在启动的时候都会使用到Configuration对象,HDFS中同样也会使用Configuration对象,所以我认为理解Configuration的基本工作原理很重要。

3 Configuration可以用来在MapReduce任务之间共享信息,当然这样共享的信息是在作业中配置,一旦作业中的map或者reduce任务启动了,configuration对象就完全独立。所以共享信息是在作业中设置的。?

时间: 2024-10-07 12:38:52

hadoop中Configuration类剖析的相关文章

hadoop中Text类 与 java中String类的区别

hadoop 中 的Text类与java中的String类感觉上用法是相似的,但两者在编码格式和访问方式上还是有些差别的,要说明这个问题,首先得了解几个概念: 字符集: 是一个系统支持的所有抽象字符的集合.字符是各种文字和符号的总称,包括各国家文字.标点符号.图形符号.数字等.例如 unicode就是一个字符集,它的目标是涵盖世界上所有国家的文字和符号: 字符编码:是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其他东西的一个集合(如号码或电脉冲)进行配对.即在符号集

Hadoop中Writable类之二

1.ASCII.Unicode.UFT-8 在看Text类型的时候,里面出现了上面三种编码,先看看这三种编码: ASCII是基于拉丁字母的一套电脑编码系统.它主要用于显示现代英语和其他西欧语言.它是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646.ASCII是7位字符集,是美国标准信息交换代码的缩写,为美国英语通信所设计.它由128个字符组成,包括大小写字母.数字0-9.标点符号.非打印字符(换行副.制表符等4个)以及控制字符(退格.响铃等)组成.从定义,很明显,单字节编码,现

Hadoop中Writable类

1.Writable简单介绍 在前面的博客中,经常出现IntWritable,ByteWritable.....光从字面上,就可以看出,给人的感觉是基本数据类型 和 序列化!在Hadoop中自带的org.apache.hadoop.io包中有广泛的Writable类可供选择.它们的层次结构如下图所示: Writable类对Java基本类型提供封装,short 和 char除外(可以存储在IntWritable中).所有的封装包包含get()  和 set() 方法用于读取或者设置封装的值.如下表

Hadoop中Writable类之四

1.定制Writable类型 Hadoop中有一套Writable实现,例如:IntWritable.Text等,但是,有时候可能并不能满足自己的需求,这个时候,就需要自己定制Writable类型. 定制分以下几步: 需要实现WritableComparable接口,因为Writable常常作为健值对出现,而在MapReduce中,中间有个排序很重要,因此,Hadoop中就让Writable实现了WritableComparable 需要实现WritableComparable的write().

hibernate中Configuration类的作用

问题:我们在获得一个SessionFactory对象的时候经常是写下面这行代码: 1 SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); 那么这行代码到底有什么作用,Configuration的对象的作用是什么? 要回答上述问题必须首先知道Configuration对象的作用. Configuration的作用是:An instance of org.hibernate.cf

Hadoop中Writable类之三

1.BytesWritable <1>定义 ByteWritable是对二进制数据组的封装.它的序列化格式为一个用于指定后面数据字节数的整数域(4个字节),后跟字节本身. 举个例子,假如有一个数组bytes,里面有两个byte,bytes[0]=3,bytes[1]=5,那么,数组序列化后,其返回一个字节数组,序列化方面,可以查看我的博客<Hadoop序列化>  ,那么序列化后,其返回一个字节书组byteSeri,byteSeri里面有多少个字节? 分析: 在定义里指出,序列化格式

Hadoop源码分析(2)——Configuration类

这篇文章主要介绍Hadoop的系统配置类Configuration. 接着上一篇文章介绍,上一篇文章中Hadoop Job的main方法为: public static void main(String[] args) throws Exception { int res = ToolRunner.run(new Configuration(), new CalculateSumJob(),args); System.exit(res); } 其中ToolRunner.run方法传入的第一个变量

在Hadoop中重写FileInputFormat类以处理二进制格式存储的整数

最近开始使用MapReduce,发现网上大部分例子都是对文本数据进行处理的,也就是说在读取输入数据时直接使用默认的TextInputFormat进行处理即可.对于文本数据处理,这个类还是能满足一部分应用场景.但是如果要处理以二进制形式结构化记录存储的文件时,这些类就不再适合了. 本文以一个简单的应用场景为例:对按照二进制格式存储的整数做频数统计.当然,也可以在此基础上实现排序之类的其他应用.实现该应用的主要难点就是如何处理输入数据.参考<权威指南·第三版>得知需要继承FileInputForm

hadoop源码解析2 - conf包中Configuration.java解析

1 Hadoop Configuration简介    Hadoop没有使用java.util.Properties管理配置文件,也没有使用Apache Jakarta Commons Configuration管理配置文件,而是使用了一套独有的配置文件管理系统,并提供自己的API,即使用org.apache.hadoop.conf.Configuration处理配置信息. org.apache.hadoop.conf目录结构如下: 2 Hadoop配置文件的格式解析    Hadoop配置文件