1. 介绍

alluxio看了很多资料,其性能让人十分期待。据说用alluxio的spark性能可以比没用的好10倍以上。用alluxio的MR和没用alluxio的MR相比肯定就提升更大了。

在开始测试前,我对结果还是比较有信心的。因为看了下alluxio在google group上有其他使用者已经对alluxio和spark性能做了测试。详情请点击:

Performance of running Spark/Mapreduce on Alluxio NO better than that on HDFS

PS: 不要被这个题目误导,这个网友一开始是提问的,觉得alluxio没有提升很大,主要原因是它用的测试负载不是很好。采用wordcount来进行测试。这个测试负载不属于I/O密集型。根据alluxio的原理可以知道,针对I/O密集型的操作,alluxio才会有最大的性能提升。因此我们后续设计测试负载的时候也要选取合适的测试负载。

重要说明!!!!
这个测试由于测试环境约束,最后结果并不是我们期望的。不过整个测试流程仍然可以作为大家学习用。我最新的测试文章见:MapReduce on alluxio性能测试

2. 测试环境

2.1 机器和内存

1台master,5台worker。虽然设定的是每台worker2500MB的内存,但是实际上可用的内存总量约为:5.7GB

2.2 配置

UFS采用HDFS,其他均采用默认配置。

3. 使用MR统计5G文件行数

3.1 关于负载数据选型

之前已经提到过,wordcount使用的CPU时间是不确定性的,如果要使用该负载则要测试具体花了多少时间再IO上。根据别人的建议,我们决定采用line count来代替word count做为实验负载。line count的是CPU使用基本是固定的。

3.2 准备数据

首先根据我们实际可用内存总量,本次实验我们决定采用一个5G的大文件来进行测试。

生成大文件可以参考资料:Quickly create a large file on a Linux system?

我们采用dd命令来生成大文件(因为我们需要里面有具体的内容可以来计算行数):
网上流传的/dev/zero来生成大文件,用wc -l 去测试行数都是为0.

#urandom是非阻塞的,生成随机内容,block size =64MB,生成5G的数据
dd if=/dev/urandom bs=64M count=80 of=hello_5G.txt

在HDFS WEB管理界面上进行验证:

3.3 mapreduce统计行数

3.3.1 编辑pom文件

首先新建个maven工程,引入hadoop client包。

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-mapreduce-examples -->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-examples</artifactId>
            <version>2.7.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-core</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-common</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>jdk.tools</groupId>
            <artifactId>jdk.tools</artifactId>
            <version>1.8</version>
            <scope>system</scope>
            <systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
        </dependency>
    </dependencies>

3.3.2 MR统计行数代码

/**
 * MR统计一个文件的行数
 * @author Wan Kaiming on 2016/9/9
 * @version 1.0
 */
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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;
public class MRLineCount {


    //Mapper主要就是每行形成一个分片,key是一行的内容,value是值1
    public static class LineMapper
            extends Mapper<Object, Text, Text, IntWritable>{

        //统计行数
        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text("总行数为:");

        public void map(Object key, Text value, Context context
        ) throws IOException, InterruptedException {

            //由于我们使用TextInputFormar,其中Key是每个数据记录在数据分片中字节偏移量 Text类型value存储的是一行的内容

            //传递给reducer的key全部固定为一个值,value就是值1,代表一行。这样reducer可以全部在一个key里面求和
            context.write(word, one);

        }
    }


    //reducer对行数进行统计求和,注意输出的key为null,我们只要计算总数即可
    public static class IntSumReducer
            extends Reducer<Text,IntWritable,Text,IntWritable> {
        private IntWritable result = new IntWritable();

        public void reduce(Text key, Iterable<IntWritable> values,
                           Context context
        ) throws IOException, InterruptedException {
            int sum = 0;
            for (IntWritable val : values) {
                sum += val.get();
            }
            result.set(sum);
            context.write(key, result);
        }
    }

    public static void main(String[] args) throws Exception {
        System.setProperty("HADOOP_USER_NAME", "root");

        Configuration conf = new Configuration();

        //设置hdfs和yarn地址
        conf.set("fs.defaultFS", "hdfs://10.45.10.33:9000");
        conf.set("yarn.resourcemanager.hostname","10.45.10.33");



        //使用默认配置创建一个Job实例
        Job job = Job.getInstance(conf, "line count");

        job.setJar("E:\\JavaProjects\\Learning\\out\\artifacts\\hadoop_test_jar\\hadoop-test.jar");
        job.setJarByClass(MRLineCount.class);

        //设置mapper,combiner和reducer
        job.setMapperClass(LineMapper.class);
        job.setCombinerClass(IntSumReducer.class);
        job.setReducerClass(IntSumReducer.class);
        //设置输出的key,value类,由于我们不需要输出key的内容,就使用NullWritable类型
        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);
    }
}

3.3.3 提交到linux上hadoop集群所需额外配置

因为是要从WIN本地提交到Linux的hadoop集群上运行,所以需要在IDEA工程里的resource目录下配置如下文件:

  1. mapred-site.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License. See accompanying LICENSE file.
-->

<!-- Put site-specific property overrides in this file. -->

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
    <property>
        <name>mapred.remote.os</name>
        <value>Linux</value>
    </property>
    <property>
        <name>mapreduce.app-submission.cross-platform</name>
        <value>true</value>
    </property>
    <property>
        <name>mapreduce.application.classpath</name>
        <value>
            /root/Downloads/hadoop-2.7.2/etc/hadoop,
            /root/Downloads/hadoop-2.7.2/share/hadoop/common/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/common/lib/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/hdfs/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/hdfs/lib/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/mapreduce/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/mapreduce/lib/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/yarn/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/yarn/lib/*
        </value>
    </property>
    <property>
        <name>mapreduce.jobhistory.address</name>
        <value>10.45.10.33:10020</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.webapp.address</name>
        <value>10.45.10.33:19888</value>
    </property>
</configuration>
  1. yarn-site.xml
<?xml version="1.0"?>
<!--
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License. See accompanying LICENSE file.
-->
<configuration>
    <!-- Site specific YARN configuration properties -->
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.resourcemanager.address</name>
        <value>10.45.10.33:8032</value>
    </property>
    <property>
        <name>yarn.application.classpath</name>
        <value>
            /root/Downloads/hadoop-2.7.2/etc/hadoop,
            /root/Downloads/hadoop-2.7.2/share/hadoop/common/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/common/lib/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/hdfs/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/hdfs/lib/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/mapreduce/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/mapreduce/lib/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/yarn/*,
            /root/Downloads/hadoop-2.7.2/share/hadoop/yarn/lib/*
        </value>
    </property>
</configuration>

3.3.4 hadoop-client工程打包成jar

点击工程按F4快捷键:

然后代码编完后build一下生成JAR

3.4 MR运行负载得到结果

运行MR程序,对5G随机内容的文件进行行数的统计,首先确保输入文件已经成功放在HDFS上。

结果:利用MR在我们的集群上对5G的大文件进行行数的统计,总共耗时2分47秒

4. Spark执行行数统计

为了方便起见,使用Spark统计行数的时候,我们直接使用其shell来操作。如果还没有安装好spark集群可以查看spark2.0集群部署

PS:我们这里使用每个WORKER 1G运行内存和1G驱动内存

在spark-shell中输入以下内容:

val s = sc.textFile("/linecount/hello_5G.txt")
s.count()

实验结果: 耗时2分05秒

PS: 这里可能会比较奇怪,为什么提升这么少。主要是我的可用内存实在太少了,其实每台node的可用内存可能只有1G左右。。。因此提升十分有限。

5.alluxio集成mapreduce

5.1 配置core-site.xml和hdfs-site.xml

配置hadoop的core-site.xml配置文件,并且添加如下内容:
该配置让你的MapReduce作业可以使用Alluxio来输入输出文件。如果你正在使用HDFS作为Alluxio的底层存储系统,同样有必要在hdfs-site.xml文件中添加这些属性:

<property>
  <name>fs.alluxio.impl</name>
  <value>alluxio.hadoop.FileSystem</value>
  <description>The Alluxio FileSystem (Hadoop 1.x and 2.x)</description>
</property>
<property>
  <name>fs.alluxio-ft.impl</name>
  <value>alluxio.hadoop.FaultTolerantFileSystem</value>
  <description>The Alluxio FileSystem (Hadoop 1.x and 2.x) with fault tolerant support</description>
</property>
<property>
  <name>fs.AbstractFileSystem.alluxio.impl</name>
  <value>alluxio.hadoop.AlluxioFileSystem</value>
  <description>The Alluxio AbstractFileSystem (Hadoop 2.x)</description>
</property>

5.2 修改hadoop-env.sh

其次, 为了让JobClient可以访问Alluxio客户端Jar文件,你可以在hadoop-env.sh文件中将HADOOP_CLASSPATH修改为:

export HADOOP_CLASSPATH=/root/Downloads/alluxio-master/core/client/target/alluxio-core-client-1.2.0-jar-with-dependencies.jar:${HADOOP_CLASSPATH}

执行完以上操作后,重启下hadoop集群

5.3 配置HDFS做为alluxio的UFS

修改alluxio-env.sh

ALLUXIO_MASTER_HOSTNAME=${ALLUXIO_MASTER_HOSTNAME:-"10.45.10.33"}
ALLUXIO_WORKER_MEMORY_SIZE=${ALLUXIO_WORKER_MEMORY_SIZE:-"2500MB"}
ALLUXIO_RAM_FOLDER=${ALLUXIO_RAM_FOLDER:-"/mnt/ramdisk"}
ALLUXIO_UNDERFS_ADDRESS=${ALLUXIO_UNDERFS_ADDRESS:-"hdfs://10.45.10.33:8020/alluxio/"}

拷贝到其他节点

alluxio copyDir alluxio-env.sh

5.4 测试

确保正常启动alluxio和hadoop之后我们就可以进行测试了。

首先把要准备输入的文件放到alluxio中。

alluxio fs mkdir /linecount
alluxio fs copyFromLocal ~/Downloads/test_txt/hello_5G.txt /linecount/hello_5G.txt

在运行时发现内存不足。。。坑爹。因此重新生成文件,采用2G的文件做linecount。

alluxio fs copyFromLocal ~/Downloads/test_txt/hello_2G.txt /linecount/hello_2G.txt

重新执行一遍spark和hadoop发现结果如下:

名称 耗时
spark 53秒
mapreduce 62秒

同样提升不明显,主要受限于测试环境。

5.4.1 分发客户端JAR包

所有节点上均要执行以下操作。

cp /root/Downloads/alluxio-master/core/client/target/alluxio-core-client-1.2.0-jar-with-dependencies.jar $HADOOP_HOME/share/hadoop/common/lib

5.4.2 修改代码中HDFS地址

修改MRLineCount的代码。主要修改HDFS地址为alluxio地址:

5.4.3 更改目录权限

可能会报如下错误:

Exception in thread "main" java.io.IOException: The ownership on the staging directory /tmp/hadoop-yarn/staging/root/.staging is not as expected. It is owned by . The directory must be owned by the submitter root or by root

更改下权限即可,与提交者保持一致:

alluxio fs chown -R root / 

测试需要用到alluxio的clinet包,我们需要使用以下命令来运行测试:

hadoop jar line_count_alluxio.jar MRLineCount   alluxio://10.45.10.33:19998/linecount/hello_2G.txt alluxio://10.45.10.33:19998/linecount/alluxio_output

5.4.4 实验结果


耗时。。。3分41秒

测试结果与我们预期不同主要是由于我使用的测试集群配置较差,本身每台平均可用内存只有1G左右,再加上运行SPARK、ALLUXIO和MR都会占用一定的JVM内存。因此ALLUXIO可用的堆外内存几乎没有了。因此在使用MR+ALLUXIO可能由于程序本身的启动和初始化等问题造成实验结果不具备说服性。

后续我们用更大的集群来做测试。

6. alluxio集成spark

6.1 关于编译

我直接使用的是预编译好的,支持hadoop2.7.x的alluxio。如果自己编译,要支持spark需要指定spark选项:

mvn clean package -Pspark -DskipTests

6.2 修改spark-env.sh

在该文件中添加如下内容:

export SPARK_CLASSPATH= /root/Downloads/alluxio-master/core/client/target/alluxio-core-client-1.2.0-jar-with-dependencies.jar:$SPARK_CLASSPATH

6.3 HDFS相关配置

如果Alluxio运行Hadoop 1.x集群之上或者使用ZK来给master容错需要的额外配置,可以参考在Alluxio上运行Spark

6.4 spark-shell测试

采用spark-shell来测试。

首先启动spark-shell

spark-shell --master spark://10.45.10.33:7077 

总计耗时2分10秒。

7. 总结

测试结果与我们预期不同主要是由于我使用的测试集群配置较差,本身每台平均可用内存只有1G左右,再加上运行SPARK、ALLUXIO和MR都会占用一定的JVM内存。因此ALLUXIO可用的堆外内存几乎没有了。因此在使用MR+ALLUXIO可能由于程序本身的启动和初始化等问题造成实验结果不具备说服性。

但是通过实验,我们已经学会了如何使用alluxio来服务我们的spark或者MR任务。