本文hadoop版本爲最新版本2.6。Configuration作爲Hadoop的一個基礎功能承擔着重要的責任,爲Yarn、HSFS、MapReduce、NFS、調度器等提供參數的配置、配置文件的分佈式傳輸(實現了Writable接口)等重要功能。java
Hadoop的加載配置文件的功能沒有采用Java本身的java.util.Properties,也沒有采用Apache Jakarta Commons中的Commons Configuration,而是本身單獨實現了一個本身的Configuration類:org.apache.hadoop.conf.Configuration,在hadoop-common-project子工程中。它的實現子類有:HdfsConfiguration、YarnConfiguration、JobConf、NfsConfiguration、FairSchedulerConfiguration等。正則表達式
1、Configuration類重要屬性講解apache
A、quitemode:boolean類型,配置信息加載過程當中,是否處於安靜模式,即有一些信息不會被記錄,默認是true;數組
B、resources:ArrayList<Resource>類型,Resource是Configuration的內部類,有兩個屬性Object resource和String name;resources是一個對象數組,用於存儲有關包含配置信息的對象;網絡
C、finalParameters:Set<String>類型,全部被聲明爲final的變量集合,聲明爲final就表示不能被後續覆蓋;app
D、loadDefaults:boolean類型,是否加載默認配置;jvm
E、REGISTRY:WeakHashMap<Configuration,Object>類型,用於多個對象的相關配置的註冊及對它們進行管理,記錄了全部的Configuration;分佈式
F、defaultResources:CopyOnWriteArrayList<String>類型,用於存儲默認的配置資源名或路徑;ide
G、properties:java內置的Properties類型,存儲全部配置信息,KV值;oop
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是設定對帶有環境變量的值所可以深刻解析的層次數,超出這個最大的層數的值將不可以解析。
2、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對象從新複製一份。
3、加載資源
能夠經過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 }
4、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 }
如上,若是容許(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類型的值了,而後能夠根據返回類型進行處理。
5、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獲取。
總之來講,1、建立對象,會加載默認資源(前提是loadResource=true);2、add資源(可選,沒有這步就是hadoop默認的資源了),會清楚原來的數據,但不會當即加載資源;3、get*方法,會觸發資源的加載(getProps),處理屬性擴展等,返回屬性對應的值;4、set*設置本身的參數。
參考:
一、蔡斌 陳湘萍 《Hadoop技術內幕---深刻將誒西Hadoop Common和HDFS結構設計與實現原理》
二、一些網絡資源