您好,登錄后才能下訂單哦!
這篇文章主要介紹了Hadoop配置信息怎么處理,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
1 配置文件簡介
配置文件是一個靈活系統不可缺少的一部分,雖然配置文件非常重要,但卻沒有標準。
1.1 Java配置文件
JDK提供了java.util.Properties類,用于處理簡單的配置文件。Properties很早就被引入到Java的類庫中,并且一直沒有什么變化。它繼承自Hashtable,表示了一個持久的屬性集,該集可保存在流中或從流中加載。屬性列表中每個鍵及其對應值都是字符串類型。
public class Properties extends Hashtable<Object,Object> { …… }
Properties處理的配置文件格式非常簡單,它只支持鍵-值對,等號左邊為鍵,右邊為值。值。形式如下:
ENTRY=VALUE
java.util.Properties中用于處理屬性列表的主要方法如下:
1、getProperty():用于在屬性列表中獲取指定鍵(參數key)對應的屬性,它有兩個形式,一個不提供默認值,另一個可以提供默認值。
2、setProperty():用于在屬性列表中設置/更新屬性值。
3、load():該方法從輸入流中讀取鍵-值對。
4、store():該方法則將Properties表中的屬性列表寫入輸出流。
相關代碼如下:
//用指定的鍵在此屬性列表中搜索屬性 public String getProperty(String key) //功能同上,參數defaultValue提供了默認值 public String getProperty(String key, String defaultValue) //最終調用Hashtable的方法put public synchronized Object setProperty(String key, String value)
使用輸入流和輸出流,Properties對象不但可以保存在文件中,而且還可以保存在其他支持流的系統中,如Web服務器。J2SE 1.5版本以后,Properties中的數據也可以以XML格式保存,對應的加載和寫出方法是loadFromXML()和storeToXML()。
1.2 Java社區提供的配置文件
由于java.util.Properties提供的能力有限,Java社區中出現了大量的配置信息讀/寫方案,其中比較有名的是Apache Jakarta Commons工具集中提供的Commons Configuration。
Commons Configuration中的PropertiesConfiguration類提供了豐富的訪問配置參數的方法。具體特性如下:
1、支持文本、XML配置文件格式。
2、支持加載多個配置文件。
3、支持分層或多級的配置。
4、提供對單值或多值配置參數的基于類型的訪問。
應該說,Commons Configuration是一個功能強大的配置文件處理工具。
1.3 Hadoop提供的配置文件
Hadoop沒有使用java.util.Properties管理配置文件,也沒有使用Apache Jakarta Commons Configuration管理配置文件,而是使用了一套獨有的配置文件管理系統,并提供自己的API。
即使用org.apache.hadoop.conf.Configuration處理配置信息。
2 HadoopConfiguration詳解
2.1 Hadoop配置文件的格式
Hadoop配置文件采用XML格式,下面是Hadoop配置文件的一個例子:
<?xml version=“1.0”?> <?xml-stylesheet type="text/xsl"href="configuration.xsl"?> <configuration> <property> <name>io.sort.factor</name> <value>10</value> <final>true</final> <description>The number of ……</description> </property> </configuration>
Hadoop配置文件的元素含義如下:
1、Hadoop配置文件的根元素是configuration,一般只包含子元素property。
2、每一個property元素就是一個配置項,配置文件不支持分層或分級。
3、每個配置項一般包括配置屬性的名稱name、值value和一個關于配置項的描述description。
4、元素final和Java中的關鍵字final類似,意味著這個配置項是“固定不變的”。final一般不出現,但在合并資源的時候,可以防止配置項的值被覆蓋。
5、Hadoop配置系統還有一個很重要的功能,就是屬性擴展。如配置項dfs.name.dir的值是${hadoop.tmp.dir}/dfs/name,其中,${hadoop.tmp.dir}會使用Configuration中的相應屬性值進行擴展。如果hadoop.tmp.dir的值是"data",那么擴展后的dfs.name.dir的值就是"data/dfs/name"。
2.2 Configuration類概述
在Configuration類中,每個屬性都是String類型的,但是值類型可能是以下多種類型,包括Java中的基本類型,如boolean(getBoolean)、int(getInt)、long(getLong)、float(getFloat),也可以是其他類型,如String(get)、java.io.File(getFile)、String數組(getStrings)等。
Configuration類還可以合并資源,合并資源是指將多個配置文件合并,產生一個配置。如果有兩個配置文件,也就是兩個資源,如core-default.xml和core-site.xml,通過Configuration類的loadResources()方法,把它們合并成一個配置。代碼如下:
Configurationconf=new Configuration(); conf.addResource("core-default.xml"); conf.addResource("core-site.xml");
如果這兩個配置資源都包含了相同的配置項,而且前一個資源的配置項沒有標記為final,那么,后面的配置將覆蓋前面的配置。
如果在第一個資源中某配置項被標記為final,那么,在加載第二個資源的時候,會有警告提示。
使用Configuration類的一般過程是:構造Configuration對象,并通過類的addResource()方法添加需要加載的資源;然后就可以使用get*方法和set*方法訪問/設置配置項,資源會在第一次使用的時候自動加載到對象中。
2.3 Configuration的成員變量
首先來看看org.apache.hadoop.conf.Configuration的類圖:
從類圖可以看到,Configuration有7個主要的非靜態成員變量。
1、quietmode:是布爾變量,用來設置加載配置的模式。如果quietmode為true(默認值),則在加載解析配置文件的過程中,不輸出日志信息。該變量只是一個方便開發人員調試的變量。
2、resources:是數組變量,該變量保存了所有通過addResource()方法添加Configuration對象的資源。
3、loadDefaults:布爾變量,用于確定是否加載默認資源,這些默認資源保存在defaultResources中。
注意:defaultResources是個靜態成員變量,通過方法addDefaultResource()可以添加系統的默認資源。
在HDFS中,會把hdfs-default.xml和hdfs-site.xml作為默認資源,并通過addDefaultResource()保存在成員變量defaultResources中;
在MapReduce中,默認資源是mapred-default.xml和mapred-site.xml。
4、properties:是java.util.Properties類型,與配置項相關的成員變量。
5、overlay:是java.util.Properties類型的,用于記錄通過set()方式改變的配置項。也就是說,出現在overlay中的鍵-值對是應用設置的,而不是通過對配置資源解析得到的。
6、finalParameters:類型是Set<String>,用來保存所有在配置文件中已經被聲明為final的鍵-值對的鍵。
提示:
A、Hadoop配置文件解析后的鍵-值對,都存放在properties中。
B、properties、overlay、finalParameters配置項相關的成員變量
7、classLoader:一個很重要的成員變量,這是一個類加載器變量,可以通過它來加載指定類,也可以通過它加載相關的資源。
了解了Configuration各成員變量的具體含義,Configuration類的其他部分就比較容易理解了,它們都是為了操作這些變量而實現的解析、設置、獲取方法。
2.4 Configuration的資源加載
2.4.1 addResource方法
資源通過對象的addResource()方法或類的靜態addDefaultResource()方法(設置了loadDefaults標志)添加到Configuration對象中。
addResource方法有4種形式:
addResource(String name)//根據classpath資源加載 addResource(URL url)//根據URL資源加載 addResource(Path file)//根據文件路徑對象加載 addResource(InputStream in)//根據一個已經打開的輸入流對象加載
同時,添加的資源并不會立即加載,只是通過reloadConfiguration()方法清空properties和finalParameters。相關代碼如下:
//以classpath資源為例 public void addResource(InputStream in) { addResourceObject(new Resource(in)); } private synchronized void addResourceObject(Resource resource) { resources.add(resource); // 添加到resources成員變量 reloadConfiguration(); } public synchronized void reloadConfiguration() { properties = null; // 觸發資源的重新加載 finalParameters.clear(); // 清除不改變參數的限制 }
2.4.2 addDefaultResource方法
靜態方法addDefaultResource也能清空Configuration對象中的數據(費靜態成員變量),這是通過類的靜態成員變量REGISTRY作為媒介進行的。
REGISTRY記錄了系統中所有的Configuration對象,代碼如下:
//靜態變量 private static final WeakHashMap<Configuration, Object> REGISTRY = new WeakHashMap<Configuration, Object>(); //記錄Configuration對象 public Configuration(boolean loadDefaults) { this.loadDefaults = loadDefaults; updatingResource = new ConcurrentHashMap<String, String[]>(); synchronized (Configuration.class) { REGISTRY.put(this, null); } }
當addDefaultResource被調用時,遍歷REGISTRY的里面的所有的Configuration對象,并在Configuration對象上調用reloadConfiguration方法,這樣就可以觸發資源的新的加載,相關代碼如下:
public static synchronized void addDefaultResource(String name) { if (!defaultResources.contains(name)) { defaultResources.add(name); for (Configuration conf : REGISTRY.keySet()) { if (conf.loadDefaults) { conf.reloadConfiguration(); } } } }
2.4.3 getProps方法
成員變量properties中的數據,直到需要的時候才會加載進來。在getPrpops方法,如果發現properties為空,將會觸發loadResources()方法加載配置資源,這里其實采用了延遲加載的設計模式,當真正需要配置數據的時候,才會開始分析配置文件,這樣可以節省系統資源,提高性能。相關代碼如下:
protected synchronized Properties getProps() { if (properties == null) { properties = new Properties(); Map<String, String[]> backup = new ConcurrentHashMap<String, String[]>(updatingResource); loadResources(properties, resources, quietmode); if (overlay != null) { properties.putAll(overlay); for (Map.Entry<Object, Object> item : overlay.entrySet()) { String key = (String) item.getKey(); String[] source = backup.get(key); if (source != null) { updatingResource.put(key, source); } } } } return properties; }
2.4.4 loadResources方法
Hadoop的配置文件都是XML形式,JAXP,Java API for XML Processing,是一種穩定、可靠的XML處理API處理XML,一般有兩種方式
1、SAX,SimpleAPI for XML,提供了一種流式的、事件驅動的XML處理方式,但是編寫比較復雜,比較適合處理大的XML文件。
2、DOM,DocumentObject Model,該方式的工作方式是:
A、將XML文檔一次性裝入內存;
B、根據文檔中定義的元素和屬性在內存中創建一個樹形結構,將文檔對象化,文檔中的每個節點對應著模型中一個對象。
C、使用對象提供的編程接口,訪問XML文檔進而操作XML文檔。
由于Hadoop的配置文件都是很小文件,因此Configuration對象使用DOM處理XML。
首先分析DOM加載部分的代碼:
private Resource loadResource(Properties properties, Resource wrapper, boolean quiet) { String name = UNKNOWN_RESOURCE; try { //得到用于創建DOM解析器的工廠 Object resource = wrapper.getResource(); name = wrapper.getName(); DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); // 忽略XML中的注釋 docBuilderFactory.setIgnoringComments(true); // 提供對XML名稱空間的支持 docBuilderFactory.setNamespaceAware(true); try { //設置Xinclude處理狀態為true,即允許XInclude機制 docBuilderFactory.setXIncludeAware(true); } catch (UnsupportedOperationException e) { ...... } //獲取解析的XML的DocumentBuilder對象 DocumentBuilder builder = docBuilderFactory.newDocumentBuilder(); Document doc = null; Element root = null; boolean returnCachedProperties = false; //根據不同資源,做預處理病調用相應形式的 if (resource instanceof URL) { //URL資源形式 ...... } else if (resource instanceof String) { //classpath資源形式 ...... } else if (resource instanceof Path) { //hadoop的PATH資源形式 ...... } else if (resource instanceof InputStream) { //輸入流的資源形式 ...... } else if (resource instanceof Properties) { //鍵值對的資源形式 ...... } else if (resource instanceof Element) { //處理configuration對象的子元素 root = (Element) resource; } .......
一般的JAXP處理都是從工廠開始,通過調用DocumentBuilderFactory的newInstance方法,獲得創建DOM解析器的工廠。這里并沒有創建出DOM解析器,只是獲得一個用于創建DOM解析器的工廠,接下來需要上述newInstance方法得到的DocumentBuilder對象進行一些設置,才能進一步通過DocumentBuilder,得到DOM解析器對象builder。
針對DocumentBuilder對象進行的主要設置包括:
A、忽略XML文檔中的注釋
B、支持XML空間
C、支持XML的包含機制。
Xinclude機制允許將XML文檔分解為多個可管理的塊,然后將一個或者多個較小的文檔組裝成一個大型文檔。也就是說,hadoop的一個配置文件中,可以利用XInclude機制將其他配置文件包含進來一并處理。例子如下:
<configuration> <xi:include href=’ssd.xml’> </configuration>
通過XInclude機制,將引用的xml文件嵌入到當前配置文件,這種方法更有利于對配置文件進行模塊化管理,同時就不需要在使用addResource方法再重新加載引用的xml文件。
設置完DocumentBuilderFactory對象以后,通過
DocumentBuilderFactory.newDocumentBuilder獲得了DocumentBuilder對象,用于從各種輸入解析XML。
在loadResource中,需要根據Configuration對象支持的4種資源分別進行處理,不過這4種情況最終調用DocumentBuilder.parse()函數,返回一個DOM解析結果。
如果輸入時一個DOM的子元素,那么將解析結果設置為輸入元素。這是為了處理下面出現的元素Configuration包含Configuration子節點的特殊情況。
成員函數loadResource的第二部分代碼,就是根據DOM的解析結果設置成Configuration的成員變量properties和finalParameters。
在確認XML的根節點是configuration以后,獲取根節點的所有子節點并對所有子節點進行處理。這里需要注意,元素configuration的子節點可以是configuration,也可以是properties。如果是configuration,則遞歸調用loadResource方法,在loadResource方法的處理過程中,子節點會被作為根節點得到繼續的處理。
如果是property子節點,那么試圖獲取property的子元素name、value和final。在成功獲得name和value的值后,根據情況后設置對象的成員變量propertis和finalParameters。相關代碼如下:
if (root == null) { if (doc == null) { if (quiet) { return null; } throw new RuntimeException(resource + " not found"); } //根節點應該是configuration root = doc.getDocumentElement(); } Properties toAddTo = properties; if (returnCachedProperties) { toAddTo = new Properties(); } if (!"configuration".equals(root.getTagName())) LOG.fatal("bad conf file: top-level element not <configuration>"); //獲取根節點的所有子節點 NodeList props = root.getChildNodes(); DeprecationContext deprecations = deprecationContext.get(); for (int i = 0; i < props.getLength(); i++) { Node propNode = props.item(i); if (!(propNode instanceof Element)) //如果子節點不是Element,忽略 continue; Element prop = (Element) propNode; if ("configuration".equals(prop.getTagName())) { //如果是子節點是configuration,遞歸調用loadResource進行處理, //這意味著configuration的子節點是configuration loadResource(toAddTo, new Resource(prop, name), quiet); continue; } //子節點是property if (!"property".equals(prop.getTagName())) LOG.warn("bad conf file: element not <property>"); NodeList fields = prop.getChildNodes(); String attr = null; String value = null; boolean finalParameter = false; LinkedList<String> source = new LinkedList<String>(); //查找name、value和final的值 for (int j = 0; j < fields.getLength(); j++) { Node fieldNode = fields.item(j); if (!(fieldNode instanceof Element)) continue; Element field = (Element) fieldNode; if ("name".equals(field.getTagName()) && field.hasChildNodes()) attr = StringInterner.weakIntern(((Text) field.getFirstChild()).getData().trim()); if ("value".equals(field.getTagName()) && field.hasChildNodes()) value = StringInterner.weakIntern(((Text) field.getFirstChild()).getData()); if ("final".equals(field.getTagName()) && field.hasChildNodes()) finalParameter = "true".equals(((Text) field.getFirstChild()).getData()); if ("source".equals(field.getTagName()) && field.hasChildNodes()) source.add(StringInterner.weakIntern(((Text) field.getFirstChild()).getData())); } source.add(name); ......
2.4.5 get*方法
get*一共代表21個方法,它們用于在configuration對象中獲取相應的配置信息。
這些配置信息可以是boolean、int、long等基本類型,也可以是其他一些hadoop常用類型,如類的信息ClassName、Classes、Class,String數組StringCollection、Strings,URL等。這些方法里最重要的是get()方法,它根據配置項的鍵獲取對應的值,如果鍵不存在,則返回默認值defaultValue。其他的方法都會依賴于get()方法,并在get()的基礎上做進步一處理。先關代碼如下:
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; }
get原生方法會調用Configuration的私有方法substitutevars方法,該方法會完成配置的屬性擴展。
屬性擴展是指配置項的值包含${key}這種格式的變量,這些變量會被自動替換成相應的值。也就是說${key}會被替換成以key為鍵的配置項的值。
注意,如果${key}替換后,得到的配置項值仍然包含變量,這個過程會繼續進行,知道替換后的值中不再出現變量為止。
最后一點需要注意的是,subsititute中進行的屬性擴展,不但可以使用保存在Configuration對象的鍵值對,而且還可以使用java虛擬的系統屬性。屬性擴展優先使用系統屬性,然后才是Configuration對象中保存的鍵值對。但是為了防止屬性擴展的獲取的死循環,故而循環20次后就終止獲取,20次后還沒有獲取到值的話,將拋出異常。
2.4.6 set*方法
相對get*來說,set*的大多數方法都很簡單,這些方法相對輸入進行類型轉換等處理后,最終都調用了set()方法,這個方法簡單調用了成員變量properties和overlay的setroperty方法,保存傳入的鍵值對。代碼如下:
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 }); } } }
3 Configurable接口
Configurable是一個很簡單的接口,也位于org.apache.hadoop.conf包中。類圖如下:
在字面理解,Configurable的含義是可配置的,如果是一個類實現了Configurable接口,意味著這個類是可配置的,也就是說,可以通過這類的一個Configuration實例,提供對象需要的一些配置信息。Hadoop的代碼中有大量的實現了Coonfigurable接口,如
org.apache.hadoop.mapred.SequenceFileInputFilter.RegexFilter。
Configurable.setConf方法何時被調用呢?一般來說,對象創建以后,就應該使用setConf方法,為對象提供進一步的初始化工作。為了簡化創建和調用setConf方法這兩個連續的步驟:
org.apache.hadoop.util.ReflectionUtils中提供了靜態方法
newInstance方法,該方法利用java反射機制,根據對象類型信息。創建一個新的相應類型的對象,然后調用ReflectionUtils中的另一個靜態方法setConf配置對象。
在setConf方法中,如果對象實現了Configurable接口,那么對象的setConf方法會被調用,并根據Configuration 的實例conf進一步初始化對象。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Hadoop配置信息怎么處理”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。