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對象就完全獨立。所以共享信息是在作業中設置的。