1.Java版本wordcount
编写Spark应用程序,本地执行是可以使用eclipse中的main方法运行的。开发应用程序步骤如下
-
第1步:创建SparkConf对象,设置Spark应用配置信息。使用setMaster()可设置Spark应用程序要连接的Spark集群的master节点的url,但是如果设置为local则代表在本地执行
SparkConf conf = new SparkConf() .setAppName("WordCountLocal") .setMaster("local")
-
第2步:创建JavaSparkContext对象。在Spark中,SparkContext是Spark所有功能的入口,无论是用Java、Scala,或者是Python编写,都必须有一个SparkContext,它的主要作用包括初始化Spark应用程序所需的一些核心组件,包括调度器(DAGSchedule、TaskScheduler),还会去到Spark Master节点进行注册;SparkContext,是Spark应用中,可以说是最最重要的一个对象。但是在Spark中,编写不同类型的Spark应用程序,使用的SparkContext是不同的:
- 如果使用scala,使用的就是原生的SparkContext对象
- 如果使用Java,使用的就是JavaSparkContext对象
- 如果是开发Spark SQL程序,那么就是SQLContext、HiveContext
- 如果是开发Spark Streaming程序,那么就是它独有的SparkContext
JavaSparkContext sc = new JavaSparkContext(conf);
-
第3步:针对数据源(本地、HDFS文件等),创建一个初始的RDD,输入源中的数据会被打散分配到RDD的每个Partition中,从而形成一个初始的分布式的数据集。因为这里是本地测试,所以就是针对本地文件。
-
SparkContext中,根据文件类型的输入源创建的RDD的方法,叫做textFile()方法。
-
在Java中,创建的普通RDD,都叫做JavaRDD
-
RDD中,有元素这种概念,如果是hdfs或者本地文件,则创建的RDD中的每一个元素就相当于是文件中的一行。
JavaRDD<String> lines = sc.textFile("C:\\Users\\admin\\Desktop\\spark.txt");
-
-
第4步:对初始RDD进行transformation操作,也就是计算操作。
-
通常操作会通过创建function,并配合RDD的map、flatMap等算子来执行
-
通常,如果function比较简单,则可以通过创建指定Function的匿名内部类实现;如果function比较复杂,则会单独创建一个类,作为实现这个function接口的类
-
由于实现wordcount功能的transformation操作需要多个步骤,故将其分为多个步骤说明
-
-
第5步:transformation操作1–使用flatMap将每一行拆分成单个单词;FlatMapFunction,有两个泛型参数,分别代表了输入和输出类型;
-
在这里,因为使用的是一行一行的文本,输入就是String类型,输出同样是String类型,因为输出的是每一行的文本
-
这里需要使用flatMap算子,该算子的作用就是将RDD的一个元素,拆分成一个或多个元素
JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>(){ private static final long serialVersionUID = 1L; @Override public Iterator<String> call(String line) throws Exception { return Arrays.asList(line.split(" ")).iterator(); } });
-
-
第6步:transformation操作2–将每一个单词映射为(单词, 1)的格式,只有这样变换,后续才能以单词作为key,来进行每个单词的出现次数的累加
-
mapToPair,其实就是将每个单词映射成一个(v1,v2)这样的Tuple2类型的元素,该类型就是scala中的tuple类型,包含两个值
-
第二个和第三个泛型参数,代表的是输出的Tuple2的第一个值和第二个值的类型
-
JavaPairRDD的两个泛型类型,分别代表了tuple元素的第一个值和第二个值的类型
JavaPairRDD<String,Integer> pairs = words.mapToPair(new PairFunction<String, String, Integer>(){ private static final long serialVersionUID = 1L; @Override public Tuple2<String, Integer> call(String word) throws Exception { return new Tuple2<String, Integer>(word, 1); } });
-
-
第7步:transformation操作3–以单词作为key,统计每个单词出现的次数
-
使用reduceByKey算子,对每个key对应的value,都进行reduce操作
-
比如JavaPairRDD中有四个元素,分别为(hello, 1) (hello, 1) (hello, 1) (world, 1),reduce操作,相当于是把第一个值和第二个值进行计算,然后再将结果与第三个值进行计算。如这里的hello,那么就相当于是,首先是1 + 1 = 2,然后再将2 + 1 = 3
-
最后返回的JavaPairRDD中的元素,也是tuple,但是第一个值就是每个key,第二个值就是key的value,reduce之后的结果,就是每个单词出现的次数
JavaPairRDD<String, Integer> wordCounts = pairs.reduceByKey(new Function2<Integer, Integer, Integer>(){ private static final long serialVersionUID = 1L; @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } });
-
-
第8步:action操作–打印统计结果数据
-
通过以上4~8步的transformation算子操作,已经统计出了单词的次数,但是,之前使用的flatMap、mapToPair、reduceByKey这种操作,都叫做transformation操作,一个Spark应用中,只有transformation操作,是不会执行的,必须要有一种叫做action操作。最后,可以使用一种叫做action操作的,比如说,foreach,来触发程序的执行
wordCounts.foreach(new VoidFunction<Tuple2<String, Integer>>(){ private static final long serialVersionUID = 1L; @Override public void call(Tuple2<String, Integer> wordCount) throws Exception { System.out.println(wordCount._1 + ":" + wordCount._2 + " times"); } });
-
-
第9步:关闭SparkContext
sc.close();
-
完整代码
public class WordCountLocal { public static void main(String[] args) { // 创建SparkConf对象 SparkConf conf = new SparkConf() .setAppName("WordCountLocal") .setMaster("local"); // 创建JavaSparkContext对象 JavaSparkContext sc = new JavaSparkContext(conf); // 创建初始RDD JavaRDD<String> lines = sc.textFile("C:\\Users\\admin\\Desktop\\spark.txt"); // 使用flatMap算子,将每一行拆分成单个单词 JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>(){ private static final long serialVersionUID = 1L; @Override public Iterator<String> call(String line) throws Exception { return Arrays.asList(line.split(" ")).iterator(); } }); // 将单词映射为(key,1)的形式 JavaPairRDD<String, Integer> pairs = words.mapToPair(new PairFunction<String, String, Integer>(){ private static final long serialVersionUID = 1L; @Override public Tuple2<String, Integer> call(String word) throws Exception { return new Tuple2<String, Integer>(word, 1); } }); // 把相同key(单词)对应的value值依次相加 JavaPairRDD<String, Integer> wordCounts = pairs.reduceByKey(new Function2<Integer, Integer, Integer>(){ private static final long serialVersionUID = 1L; @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } }); // 使用foreach算子打印统计结果数据 wordCounts.foreach(new VoidFunction<Tuple2<String, Integer>>(){ private static final long serialVersionUID = 1L; @Override public void call(Tuple2<String, Integer> wordCount) throws Exception { System.out.println(wordCount._1 + ": " + wordCount._2 + "times"); } }); sc.close(); } }
2.Scala版本wordcount
Scala版本的wordcount与Java版本的实现原理是一样的,只是在代码实现上有些许不同。
object WordCount {
def main(args: Array[String]){
// 创建SparkConf、SparkContext
val conf = new SparkConf().setAppName("WordCount").setMaster("local")
val sc = new SparkContext(conf)
// 创建初始RDD
val lines = sc.textFile("C:/Users/admin/Desktop/spark.txt", 1)
// 每一行拆分成单个单词
val words = lines.flatMap(line => line.split(" "))
// 将单词映射为(key,1)的形式
val pairs = words.map(word => (word, 1))
// 把相同key(单词)对应的value值依次相加
val wordCounts = pairs.reduceByKey(_ + _)
// 使用foreach算子打印统计结果数据
wordCounts.foreach(wordCount => println(wordCount._1 + ": " + wordCount._2 + " times"))
}
}
3.pom.xml
下边贴出了Java环境下Spark和Hadoop版本依赖的pom文件,Scala版本的pom与其类似。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.spark.study.core</groupId>
<artifactId>spark-study-java</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spark-study-java</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spark.version>2.3.0</spark.version>
<hadoop.version>2.7.3</hadoop.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/main/test</testSourceDirectory>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass></mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>java</executable>
<includeProjectDependencies>true</includeProjectDependencies>
<includePluginDependencies>false</includePluginDependencies>
<classpathScope>compile</classpathScope>
<mainClass>cn.spark.study.core.App</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.spark-shell提交
除了本地执行编写好的Spark应用外,还可以通过spark-shell的方式提交集群运行
spark-shell的内容如下:
/usr/local/spark/bin/spark-submit \
--class cn.spark.sparktest.core.WordCountCluster \
--num-executors 3 \
--driver-memory 100m \
--executor-memory 100m \
--executor-cores 3 \
/usr/local/SparkTest-0.0.1-SNAPSHOT-jar-with-dependencies.jar \