口试题揭秘:10G文件字符统计,Hadoop分布式处理头脑和实战! ...

打印 上一主题 下一主题

主题 1723|帖子 1723|积分 5173

前言

我想,作为开发人员,即便你不是大数据开发工程师,也应该听说过Hadoop。而且我以为,岂论你是从事哪个开发岗位,都应该对它有所相识,因为Hadoop的头脑已经渗透在众多技术中。
如果你之前对Hadoop还不太熟悉,盼望通过本文对Hadoop的先容、根本原理以及简朴的实践案例让你相识它,而且在你之后的工作中有所资助。
Hadoop :我可以处理海量数据

什么是 Hadoop?怎么处理大数据?在这里给没有概念的小伙伴先容一下。

如上图,Hadoop 实在就是一个用 Java 编写的项目,包含了 HDFS、MapReduce 以及 Yarn 这几个核心组件。本文重点先容 HDFS 和 MapReduce,Yarn 作为资源调度组件,本文就不重点阐明了。
说起 HDFS 和 MapReduce,还要从Google的几篇论文开始聊起,《花了1个月学大数据,我想说…》中有详细阐明,这里长话短说。
HDFS : 存储归我

HDFS 全称为分布式文件体系 (Hadoop Distributed File System),在大数据范畴中用来存储海量的数据。
没错,只要是涉及到分布式,任何一个大问题都能分成多个小问题逐一办理,即分而治之
HDFS 的分而治之就是将一个体量巨大的文件切分成多个文件(数据块)分布在不同服务器上存储。这样即便是TB到PB级别的大文件,也不会对存储空间以及IO性能有太大影响。下面是 HDFS 的架构。

简朴来说,一个大文件分布在不同节点一定需要统一管理,这里的节点在 HDFS 中就是 DataNode ,而统一管理的就是 NameNode。为了不偏离文章主题,这里不对此深入阐明。
MapReduce :计算归我

在 MapReduce 没有出现之前,计算都是单机。如果是对大量数据进行计算,那对内存和CPU都需要特别高的要求。在2000年代主流内存容量最多为几十兆到几百兆,显然在单机处理不了大量数据的计算。
那 MapReduce 是怎么处理的?
既然大文件可以基于 HDFS 分布在不同节点,那计算程序也分布到这些节点计算,最后把结果汇总不就好了?
MapReduce 的核生理念就是这样的:“相比移动数据,移动计算更为经济高效。”,这也影响了后续众多的技术。
MapReduce 的计算流程大概如下图,有分布式体系开发经验的小伙伴应该不难明白。

对 Hadoop 有了根本概念后,下面用一个非常典型的口试题进行实操,看看如何基于Hadoop处理“统计10G大小的文件中字符出现的次数”。
Hadoop 环境搭建

Hadoop自己就是一个Java项目,所以,对于Javaer来说搭建起来也比力容易。下面是搭建步调:

  • 前去Hadoop官方下载二进制包并在当地解压。
  • 配置环境变量,实在就是指向到Hadoop的几个目录,大概是这样:
    1. export JAVA_HOME=/xxx
    2. export HADOOP_HOME=/xxx
    3. export HADOOP_HOME=/xxx
    4. export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
    5. export HADOOP_MAPRED_HOME=$HADOOP_HOME
    6. export HADOOP_COMMON_HOME=$HADOOP_HOME
    7. export HADOOP_HDFS_HOME=$HADOOP_HOME
    8. export YARN_HOME=$HADOOP_HOME
    复制代码
  • Hadoop是通过SSH进行节点间的通信和管理,所以各节点需要开启远程登录。以Mac体系为例,需要在下图中开启远程登录而且开启免密码登录。

    免密码登录需要执行以下下令创建秘钥,并放在ssh授权目录。
    1.         ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
    2.         cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
    复制代码
  • 配置 Hadoop 包下 ./etc/hadoop 的几个文件

    • hadoop-env.sh 配置 JDK 环境
    1. export JAVA_HOME=/xxx
    复制代码
      

    • core-site.xml 配置 HDFS 服务发布地点和临时文件目录
    1. <configuration>
    2.   <property>
    3.     <name>fs.defaultFS</name>
    4.     <value>hdfs://localhost:9000</value>
    5.   </property>
    6.   <property>
    7.     <name>hadoop.tmp.dir</name>
    8.     <value>file:/xxx/tmp</value>
    9.   </property>
    10. </configuration>
    复制代码
      

    • hdfs-site.xml 配置数据存储相关内容
    1. <configuration>
    2.   <property>
    3.     <name>dfs.replication</name>
    4.     <value>1</value>
    5.   </property>
    6.   <property>
    7.     <name>dfs.permissions</name>
    8.     <value>false</value>   
    9.   </property>
    10.   <property>
    11.     <name>dfs.namenode.name.dir</name>
    12.     <value>file:/xxx/namenode</value>
    13.   </property>
    14.   <property>
    15.     <name>dfs.datanode.data.dir</name>
    16.     <value>file:/xxx/datanode</value>
    17.   </property>
    18. </configuration>
    复制代码
      

    • mapred-site.xml 配置MapReduce使命调度平台
    1.         <configuration>
    2.     <property>
    3.          <name>mapreduce.framework.name</name>
    4.          <value>yarn</value>
    5.     </property>
    6.     <property>
    7.          <name>mapreduce.application.classpath</name>
    8.          <value>/xxx</value>
    9.     </property>
    10.     <property>
    11.       <name>mapreduce.shuffle.enable.ssl</name>
    12.       <value>false</value>
    13.     </property>
    14. </configuration>
    复制代码
      

    • yarn-site.xml 配置
    1. <configuration>
    2.     <property>
    3.             <name>yarn.nodemanager.aux-services</name>
    4.             <value>mapreduce_shuffle</value>
    5.         </property>
    6. </configuration>
    复制代码

  • 在 Hadoop 包下的./bin目录下执行下面的下令初始化 hdfs
    1. ./hdfs namenode -format
    复制代码
  • 在 Hadoop 包下的./sbin目录下执行下面的下令启动hdfs和yarn。
    1. ./start-dfs.sh
    2. ./start-yarn.sh
    复制代码
启动成功后分别通过127.0.0.1:9870 、127.0.0.1:8088 访问,Hadoop就算搭建完成了。


统计10G大小的文件中字符出现的次数

预备10G的文件

我这里利用python脚本生成了以空格分割单词的文件,内容大概如下:

然后多次执行下令 cat random_words_in_chunks.txt >> 10G_words.txt 快速将文件扩大至10G。
上传至HDFS

通过执行下面的下令将这10G的文件上传到 HDFS 的test目录。
  1. #创建文件目录
  2. hadoop fs -mkdir -p hdfs://127.0.0.1:9000/test
  3. #上传文件
  4. hadoop fs -put 10G_words.txt /test
复制代码
执行成功后就可以通过 Web UI界面看到数据。

编写 MapReduce 程序

之所以称之为 MapReduce,是因为 MapReduce 将计算分为两个阶段,一个Map阶段,一个Reduce阶段
我们要统计字符出现的次数,只需要关注Map和Reduce分别做什么操作。


  • 通过Map阶段可以将字符转换成我们熟悉的键值对,key为字符,value为数字,比方(“字符1”,1)
  • 通过Reduce阶段可以将同样key的value进行叠加,从而到达目的
接下来看代码的具体编写。
创建一个Maven项目,引入相关依赖。
  1. <dependencies>
  2.     <dependency>
  3.         <groupId>org.apache.hadoop</groupId>
  4.         <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
  5.         <version>3.3.6</version>
  6.     </dependency>
  7.     <dependency>
  8.         <groupId>org.apache.hadoop</groupId>
  9.         <artifactId>hadoop-common</artifactId>
  10.         <version>3.3.6</version>
  11.     </dependency>
  12.     <dependency>
  13.         <groupId>org.apache.hadoop</groupId>
  14.         <artifactId>hadoop-hdfs-client</artifactId>
  15.         <version>3.3.6</version>
  16.     </dependency>
  17. </dependencies>
复制代码
MapReduce为我们提供了Mapper类来完成Map阶段的计算使命,所以,我们只需要继承Mapper类编写逻辑即可。代码如下
  1. public class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable> {
  2.     private final static IntWritable one = new IntWritable(1);
  3.     private Text word = new Text();
  4.     public void map(Object key, Text value, Context context
  5.     ) throws IOException, InterruptedException {
  6.         StringTokenizer itr = new StringTokenizer(value.toString());
  7.         while (itr.hasMoreTokens()) {
  8.             word.set(itr.nextToken());
  9.             context.write(word, one);
  10.         }
  11.     }
  12. }
复制代码
Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> 的泛型分别是计算使命的输入的键值对类型以及输出的键值对类型。
Map阶段的输入还只是读取的一行字符,所以需要通过StringTokenizer将这些字符以空格进行分割得到每个字符,然后遍历转化为(“字符”,1)的键值对作为输出
本次上传到 HDFS 的10G文件被分割为77个数据块,那么终极会有77个Map计算使命以及结果输出。
同样,MapReduce为我们提供了Reducer类来完成Reduce阶段的计算使命,所以,我们只需要继承Reducer类编写逻辑即可。代码如下
  1. public class IntSumReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
  2.     private IntWritable result = new IntWritable();
  3.     public void reduce(Text key, Iterable<IntWritable> values,
  4.                        Context context
  5.     ) throws IOException, InterruptedException {
  6.         int sum = 0;
  7.         for (IntWritable val : values) {
  8.             sum += val.get();
  9.         }
  10.         result.set(sum);
  11.         context.write(key, result);
  12.     }
  13. }
复制代码
Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT> 的泛型同Mapper。
不同Map阶段输出的雷同key终极会被同一个Reduce使命作为输入,在编码时需要将雷同输出的值进行叠加,终极完成每个字符出现的次数。
除了要编写Mapper和Reducer类,还需要写一个程序入口,即main方法。代码终极会编译为jar包交给yarn,终极会有多少使命,资源的申请以及使命的分发都在这个类完成。代码如下:
  1. public class WordCount {
  2.   public static void main(String[] args) throws Exception {
  3.     Configuration conf = new Configuration();
  4.     String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
  5.     if (otherArgs.length < 2) {
  6.       System.err.println("Usage: wordcount <in> [<in>...] <out>");
  7.       System.exit(2);
  8.     }
  9.     Job job = Job.getInstance(conf, "word count");
  10.     job.setJarByClass(WordCount.class);
  11.     job.setMapperClass(TokenizerMapper.class);
  12.     job.setCombinerClass(IntSumReducer.class);
  13.     job.setReducerClass(IntSumReducer.class);
  14.     job.setOutputKeyClass(Text.class);
  15.     job.setOutputValueClass(IntWritable.class);
  16.     for (int i = 0; i < otherArgs.length - 1; ++i) {
  17.       FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
  18.     }
  19.     FileOutputFormat.setOutputPath(job,
  20.       new Path(otherArgs[otherArgs.length - 1]));
  21.     System.exit(job.waitForCompletion(true) ? 0 : 1);
  22.   }
  23. }
复制代码
启动分布式计算

以上的代码编写完后,将程序打成jar包,然后执行下面的下令就可以启动分布式计算。
  1. hadoop jar mr-wordcount-1.0-SNAPSHOT.jar WordCount  /test /testresult
复制代码
下令中的 WordCount 是 main 方法类。背面的两个路径是HDFS的目录,一个代表要计算的数据在哪里,一个代码计算后的结果存在哪里。
下令执行后,可以通过yarn的 web ui 大概控制台看到使命分发和执行。


计算过程就如下图一样,程序被分发到数据地点的节点,然后计算结果最后进行汇总。

MapReduce 使命执行完成后,在 hdfs 的 web ui 界面就可以看到计算结果。

至此,10G文件字符统计就通过Hadoop的分而治之快速完成。而作为开发人员来讲,只需要编写两段极简朴的代码,不要思量资源如何分配、程序如何移动、计算结果如何聚合就完成了一个分布式体系。
总结


  • Hadoop是基于Java开发的项目,所以对于Javaer来说容易上手。
  • Hadoop 的出现大大降低了分布式编程的难度,它不但单是一个项目,更重要的是它的头脑和理念:“分而治之”和“移动计算”。
  • Hadoop 的影响不仅仅是大数据范畴,在它之后的分布式数据库、微服务等等都受其影响。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

东湖之滨

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表