學習hadoop也有一段時間了,本來一直在dataguru的日志系統上寫一些學習工作的總結,但是比較的隨意,沒有太多的總結性。到最后因為那邊的編輯系統不太好使,就決定轉過來了。好了,廢話少說,開始開源的hadoop項目源碼學習旅途。
這個系列的博客將根據《hadoop權威指南》(第三版)的第五章開始,逐步分析學習文章中第一次出現的類。
第五章開始,就是Configuration,那就從這里入手。
hadoop中,組件配置是由Hadoop的Configuration的一個實例實現。(在源碼包的org.apache.hadoop.conf中可以找到)先上個類圖:這只是部分的,Configuraation涉及的方法很多,不一一例舉了。
在這里面我們看到的是整個hadoop的核心包的conf package里面涉及到全部類和接口。
在書中,我們可以看到一個XML文檔以及一個利用configuration實例來讀取XML文檔的程序。這里搬過來,方便下面的學習分析。
java實例代碼如下:<?xml version="1.0"?> <configuration> <property> <name>color</name> <value>yellow</value> <description>Color</description> </property> <property> <name>size</name> <value>10</value> <description>Size</description> </property> <property> <name>weight</name> <value>heavy</value> <final>true</final> <description>Weight</description> </property> <property> <name>size-weight</name> <value>${size},${weight}</value> <description>Size and weight</description> </property> </configuration>
Configuration conf = new Configuration(); conf.addResource("configuration-1.xml"); assertThat(conf.get("color"), is("yellow")); assertThat(conf.getInt("size", 0), is(10)); assertThat(conf.get("breadth", "wide"), is("wide"));
在這里我們主要首先關注一個get(String name)方法.
public String get(String name) { return substituteVars(getProps().getProperty(name)); }
首先應該從addResource()說起,如conf.addResource("configuration-1.xml"),這里實現了類似懶加載的方法來實現資源的讀取,也就是說在add完成XML文件的時候,是不會去更新屬性列表的,只有當有需要讀取屬性值的時候才會進行資源的加載。要注意的是,在addResource()的時候,會將給定的資源放到一個資源private ArrayList 里面,然后會調用reloadConfiguration方法:
public synchronized void reloadConfiguration() { properties = null; // 清除之前加載進來的全部屬性 finalParameters.clear(); // 因為可以在屬性里面標注final屬性,所以在這里可以將全部的final屬性全部也清除掉。 }
讀取屬性的時候,就會先調用getProps()方法,這個方法里面調用了Configuration類里面的一個核心方法,loadResources():
private void loadResources(Properties properties, ArrayList resources, boolean quiet) { //三個參數,properties用來存儲加載出來的屬性,resources表明資源列表, 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); } }
這里提供了三張資源加載的方式,但是最后是由loadResource(properties, resource, quiet)這一方法來實現的。這里主要的實現是利用java DOM API 對所有的resource進行遍歷,將全部的屬性值加載到這里面來。初始化代碼如下:
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); //實例化一個工廠類docBuilderFactory.setIgnoringComments(true); //忽略開頭的命名空間等信息 docBuilderFactory.setNamespaceAware(true); try { docBuilderFactory.setXIncludeAware(true); } catch (UnsupportedOperationException e) { LOG.error("Failed to set setXIncludeAware(true) for parser " + docBuilderFactory + ":" + e, e); }DocumentBuilder builder = docBuilderFactory.newDocumentBuilder(); Document doc = null; Element root = null;因為有string url inputstream三種格式的參數傳進來,前兩種都會轉成URL的形式送到builder.parse()來解析。
由上面大家也看到了,采用了DOM的解析方式。熟悉XML的人都知道,還有一種比較流行的解析方式,SAX解析。在這里,相對的XML文檔不會太多,所以解析的效果也不會有明顯的差異,都是可行的。但是DOM的解析方式更為的直觀、直接。
部分的for循環當中的代碼:
NodeList fields = prop.getChildNodes(); String attr = null; String value = null; boolean finalParameter = false; 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 = ((Text)field.getFirstChild()).getData().trim(); if ("value".equals(field.getTagName()) && field.hasChildNodes()) value = ((Text)field.getFirstChild()).getData(); if ("final".equals(field.getTagName()) && field.hasChildNodes()) finalParameter = "true".equals(((Text)field.getFirstChild()).getData()); }
最后經過
properties.setProperty(attr, value);放入結合當中,這樣就產生了get()方法調用substituteVars方法的getPros()的方法。
在學習上述的代碼的時候,我慢慢體會到了,java私有方法和公有方法的一些使用的要點。那就是在使用私有方法的時候,應該盡可能的降低其對於全局變量的依賴性,可以在調用私有方法前盡可能的去掉一些不要的邏輯,讓私有方法好好的工作。像configuration這個類,從addResource到loadResource,都是極盡可能的消除方法后端的一些影響因素,將更多的邏輯分擔出來,使得代碼的閱讀更加的簡單明了,這是一個程序員應該有的品質吧。
最后要說一下,這里面還有一個用於屬性導出的函數,也是一個比較值得學習的方法,這里就把代碼貼出來。
public static void dumpConfiguration(Configuration conf, Writer out) throws IOException { Configuration config = new Configuration(conf,true); config.reloadConfiguration(); JsonFactory dumpFactory = new JsonFactory(); JsonGenerator dumpGenerator = dumpFactory.createJsonGenerator(out); dumpGenerator.writeStartObject(); dumpGenerator.writeFieldName("properties"); dumpGenerator.writeStartArray(); dumpGenerator.flush(); for (Map.Entry<Object,Object> item: config.getProps().entrySet()) { dumpGenerator.writeStartObject(); dumpGenerator.writeStringField("key", (String) item.getKey()); dumpGenerator.writeStringField("value", config.get((String) item.getKey())); dumpGenerator.writeBooleanField("isFinal", config.finalParameters.contains(item.getKey())); dumpGenerator.writeStringField("resource", config.updatingResource.get(item.getKey())); dumpGenerator.writeEndObject(); } dumpGenerator.writeEndArray(); dumpGenerator.writeEndObject(); dumpGenerator.flush(); }
好了,第一期的就先到這里吧。收拾下,准備下一個,Tool接口!