hadoop源碼學習(一)--configuration類詳解


學習hadoop也有一段時間了,本來一直在dataguru的日志系統上寫一些學習工作的總結,但是比較的隨意,沒有太多的總結性。到最后因為那邊的編輯系統不太好使,就決定轉過來了。好了,廢話少說,開始開源的hadoop項目源碼學習旅途。

這個系列的博客將根據《hadoop權威指南》(第三版)的第五章開始,逐步分析學習文章中第一次出現的類。

第五章開始,就是Configuration,那就從這里入手。

hadoop中,組件配置是由Hadoop的Configuration的一個實例實現。(在源碼包的org.apache.hadoop.conf中可以找到)先上個類圖:這只是部分的,Configuraation涉及的方法很多,不一一例舉了。

configuration

在這里面我們看到的是整個hadoop的核心包的conf package里面涉及到全部類和接口。

 

在書中,我們可以看到一個XML文檔以及一個利用configuration實例來讀取XML文檔的程序。這里搬過來,方便下面的學習分析。

<?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>
java實例代碼如下:
    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接口!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM