Hadoop编程_Configuration类

本文深入探讨Hadoop中Configuration类的工作原理,包括如何加载配置文件、解析属性及其实现细节。

根据《hadoop权威指南》(第三版)

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();
  }
复制代码
### Hadoop编程基础与示例 Hadoop 是一种用于处理大规模数据集的分布式计算框架,其核心组件包括 Hadoop Distributed File System (HDFS) 和 MapReduce 计算模型。以下是关于如何编写基于 Hadoop 的简单应用程序的具体说明。 #### 添加 Maven 依赖项 为了开发 Hadoop 应用程序,首先需要在项目的 `pom.xml` 文件中引入必要的依赖项。以下是一个典型的 Maven 配置: ```xml <dependencies> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>3.2.1</version> </dependency> </dependencies> ``` 此部分描述了如何通过 Maven 构建工具来管理 Hadoop 客户端库[^3]。 --- #### 基本 Hadoop FileSystem 操作 下面展示了一些常见的 Hadoop FileSystem API 调用方式,这些方法可以帮助开发者执行诸如创建目录、上传文件等操作。 ```java import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; public class HDFSDemo { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); conf.set("fs.defaultFS", "hdfs://localhost:9000"); try (FileSystem fs = FileSystem.get(conf)) { Path path = new Path("/example.txt"); // 创建新文件 boolean createdNewFile = fs.createNewFile(path); if (createdNewFile) { System.out.println("成功创建文件:" + path.toString()); } else { System.out.println("文件已存在!"); } } } } ``` 上述代码片段展示了如何连接到本地运行的 HDFS 实例并尝试创建一个新的文本文件[^1]。 --- #### MapReduce 示例:单词计数器 MapReduce 是 Hadoop 中的核心概念之一,它允许用户定义两个主要阶段——映射(map)和归约(reduce)。这里提供了一个经典的“单词统计”案例实现。 ##### Mapper 负责读取输入键值对并将它们转换成中间形式供后续 Reduce 步骤使用。 ```java import java.io.IOException; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text word = new Text(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString().toLowerCase(); // 将整行转为小写 for (String token : line.split("\\W+")) { // 利用正则表达式分割字符串 if (!token.isEmpty()) { word.set(token); // 设置当前词作为输出key context.write(word, one); // 输出每遇到一次就增加计数值 } } } } ``` ##### Reducer 这个接收来自 Mapper 的所有相同 Key 对应 Value 并汇总结果。 ```java import java.io.IOException; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> { @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int sum = 0; for (IntWritable val : values) { sum += val.get(); // 累加所有的value值得到总数目 } context.write(key, new IntWritable(sum)); // 写入最终的结果给下一层或者直接保存至磁盘上 } } ``` ##### 主驱动程序 最后一步是设置作业参数并通过 JobTracker 提交任务。 ```java import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class WordCountDriver { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "Word Count Example"); job.setJarByClass(WordCountDriver.class); job.setMapperClass(WordCountMapper.class); job.setCombinerClass(WordCountReducer.class); job.setReducerClass(WordCountReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } } ``` 以上三段代码共同构成了完整的单词频率分析解决方案[^2]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值