本文hadoop版本为最新版本2.6。Configuration做为Hadoop的一个基础功能承担着重要的责任,为Yarn、HSFS、MapReduce、NFS、调度器等提供参数的配置、配置文件的分布式传输(实现了Writable接口)等重要功能。

  Hadoop的加载配置文件的功能没有采用Java自己的java.util.Properties,也没有采用Apache Jakarta Commons中的Commons Configuration,而是自己单独实现了一个自己的Configuration类:org.apache.hadoop.conf.Configuration,在hadoop-common-project子工程中。它的实现子类有:HdfsConfiguration、YarnConfiguration、JobConf、NfsConfiguration、FairSchedulerConfiguration等。

  一、Configuration类重要属性讲解

  A、quitemode:boolean类型,配置信息加载过程中,是否处于安静模式,即有一些信息不会被记录,默认是true;

  B、resources:ArrayList<Resource>类型,Resource是Configuration的内部类,有两个属性Object resource和String name;resources是一个对象数组,用于存储有关包含配置信息的对象;

  C、finalParameters:Set<String>类型,所有被声明为final的变量集合,声明为final就表示不能被后续覆盖;

  D、loadDefaults:boolean类型,是否加载默认配置;

  E、REGISTRY:WeakHashMap<Configuration,Object>类型,用于多个对象的相关配置的注册及对它们进行管理,记录了所有的Configuration;

  F、defaultResources:CopyOnWriteArrayList<String>类型,用于存储默认的配置资源名或路径;

  G、properties:java内置的Properties类型,存储所有配置信息,KV值;

  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是设定对带有环境变量的值所能够深入解析的层次数,超出这个最大的层数的值将不能够解析。

  二、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对象重新复制一份。

  三、加载资源

  可以通过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. }

  四、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类型的值了,然后可以根据返回类型进行处理。

  五、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获取。

  总之来说,一、创建对象,会加载默认资源(前提是loadResource=true);二、add资源(可选,没有这步就是hadoop默认的资源了),会清楚原来的数据,但不会立即加载资源;三、get*方法,会触发资源的加载(getProps),处理属性扩展等,返回属性对应的值;四、set*设置自己的参数。

  

参考:

  1、蔡斌 陈湘萍 《Hadoop技术内幕---深入将诶西Hadoop Common和HDFS结构设计与实现原理》

  2、一些网络资源

Hadoop源码之Configuration的更多相关文章

  1. [Hadoop源码解读](六)MapReduce篇之MapTask类

    MapTask类继承于Task类,它最主要的方法就是run(),用来执行这个Map任务. run()首先设置一个TaskReporter并启动,然后调用JobConf的getUseNewAPI()判断 ...

  2. 详细讲解Hadoop源码阅读工程(以hadoop-2.6.0-src.tar.gz和hadoop-2.6.0-cdh5.4.5-src.tar.gz为代表)

    首先,说的是,本人到现在为止,已经玩过.                   对于,这样的软件,博友,可以去看我博客的相关博文.在此,不一一赘述! Eclipse *版本 Eclipse *下载 Jd ...

  3. Hadoop源码学习笔记之NameNode启动场景流程一:源码环境搭建和项目模块及NameNode结构简单介绍

    最近在跟着一个大佬学习Hadoop底层源码及架构等知识点,觉得有必要记录下来这个学习过程.想到了这个废弃已久的blog账号,决定重新开始更新. 主要分以下几步来进行源码学习: 一.搭建源码阅读环境二. ...

  4. Hadoop源码学习笔记(6)——从ls命令一路解剖

    Hadoop源码学习笔记(6) ——从ls命令一路解剖 Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的. 我们就从原头开始,然后一步步追查. 我们先选中ls命 ...

  5. Hadoop源码学习笔记(4) ——Socket到RPC调用

    Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要 ...

  6. Hadoop源码学习笔记(3) ——初览DataNode及学习线程

    Hadoop源码学习笔记(3) ——初览DataNode及学习线程 进入了main函数,我们走出了第一步,接下来看看再怎么走: public class DataNode extends Config ...

  7. Hadoop源码学习笔记(1) ——第二季开始——找到Main函数及读一读Configure类

    Hadoop源码学习笔记(1) ——找到Main函数及读一读Configure类 前面在第一季中,我们简单地研究了下Hadoop是什么,怎么用.在这开源的大牛作品的诱惑下,接下来我们要研究一下它是如何 ...

  8. Hadoop源码分析之数据节点的握手,注册,上报数据块和心跳

    转自:http://www.it165.net/admin/html/201402/2382.html 在上一篇文章Hadoop源码分析之DataNode的启动与停止中分析了DataNode节点的启动 ...

  9. Hadoop源码解读系列目录

    Hadoop源码解读系列 1.hadoop源码|common模块-configuration详解2.hadoop源码|core模块-序列化与压缩详解3.hadoop源码|core模块-远程调用与NIO ...

随机推荐

  1. eclipse的一些初始化设置

    eclipse下载地址:http://www.eclipse.org/ 添加java注释模板 codetemplates.xml:Window->Preferences->Java-> ...

  2. Android Eclipse真机调试 过滤器filter没有显示

    问题: 用真机连上电脑做安卓调试是,工程对应的过滤器会不定期的显示或者不显示.wo the fuck. 解决办法: 为这个工程手动建一个filter

  3. WIN7下USB多点触摸,一次发多个数据包的延迟问题,重要!

    这个问题很常见, 花了差不多一个星期时间来解决.硬件相关的东西太多坑了,而且这些坑不像代码那样可见.   使用混合模式,每次最多报告2个点.如果是5点则需要上报三次. 问题就来了,atmel的CTP最 ...

  4. 总结一下工作中遇到的NPOI以及在ASP.NET MVC中的使用

    1.前言 相信大家在工作中经常要遇到一些导入导出Execl操作.学习贵在分享,分享使人快乐,园子里的前辈已经有很多好的文章,鄙人也是能力有限,在这里把这些好的文章总结,方便以后再工作中使用. NPOI ...

  5. 定一个小目标:明年1024能成功转行web前端,光荣地成为一个程序员!

    第一次在博客园写博,我为什么要选择这里吗? 据说博客园这里的IT大牛如云,作为一个求知若渴的小白,我屁颠屁颠的跟着过来了. 于是今天早上兴高采烈的注册了账号,迫不及待的打开我的博客,呃!注册账号成功了 ...

  6. jquery封装常用方法

    var git = { //true表示格式正确 checkEmail: function (str) { -]{,})(\S*)/g) == null) { return false; } else ...

  7. js中基本操作

    1.操作标签值 <!DOCTYPE html> <html> <meta charset="utf-8"> <meta http-equi ...

  8. [设计模式] javascript 之 单件模式

    单件模式说明 1. 说明:单件模式,就是静态化的访问中已经实例化的对象,这个对象只能通过一个唯一的入口访问,已经实例或待实例化的对象:面向对象语言如Java, .Net C#这样的服务端动态语言里,能 ...

  9. Convention插件

    考虑使用COnvention插件可以进行零配置而且插件进行很多规范的约定也可以对开发合作当中按着它相应的规律开发..感觉也挺方便管理的.下面简单介绍它的使用. 首先我们需要使用到的jar包: Java ...

  10. 用Random类输出验证码

    package dx; import java.text.DecimalFormat; import java.util.Random; public class DxL { //创建类 public ...