Spark快速入门

Spark快速入门

一. Spark概述(了解)

(一)Spark简介

    Apache Spark是用于大规模数据处理的统一分析引擎。

    Spark基于内存计算,提高了在大数据环境下数据处理的实时性,同时保证了高容错性和高可伸缩性,允许用户将Spark部署在大量硬件之上,形成集群。

(二)Spark vs Hadoop
  1. MapReduce框架采用非循环式的数据流模型, 中间计算结果存在HDFS磁盘上, 带来了大量的数据复制、磁盘IO和序列化开销, 延迟大, Task以进程方式维护, 任务启动慢;
  2. RDD中间运算结果存在内存中 , 延迟小, Task以线程方式维护, 任务启动快,Spark主要用于替代Hadoop中的MapReduce计算模型,存储依然可以使用HDFS。
(三)Spark的优点
  1. Spark基于内存的运算比MapReduce要快100倍以上,基于硬盘的运算也要快10倍以上。Spark实现了高效的DAG执行引擎,可以通过基于内存来高效处理数据流。

  2. 易用

    Spark支持Java、Python、R和Scala的API,还支持超过80种高级算法,用户可快速构建不同的应用。Spark支持交互式的Python和Scala的shell。

  3. 通用

    Spark提供了统一的解决方案。Spark可以用于批处理、交互式查询(Spark SQL)、实时流处理(Spark Streaming)、机器学习(Spark MLlib)和图计算(GraphX)。

  4. 兼容性

    Spark可以非常方便地与其他的开源产品进行融合。比如,Spark可用Hadoop的YARN和Apache Mesos作为它的资源管理和调度器,可处理所有Hadoop支持的数据,包括HDFS、HBase和Cassandra等。Spark实现了Standalone作为内置的资源管理和调度框架。此外,Spark还提供了在EC2上部署Standalone的Spark集群的工具。

(四)Spark组件
  1. Spark Core

    实现了 Spark 的基本功能,包含RDD、任务调度、内存管理、错误恢复、与存储系统交互等模块。

  2. Spark SQL

    是 Spark 用来操作结构化数据的程序包。通过 Spark SQL,我们可以使用 SQL操作数据。

  3. Spark Streaming

    是 Spark 提供的对实时数据进行流式计算的组件。提供了用来操作数据流的 API。

  4. Spark MLlib

    提供常见的机器学习(ML)功能的程序库。包括分类、回归、聚类、协同过滤等,还提供了模型评估、数据导入等额外的支持功能。

  5. GraphX(图计算)

    GraphX是Spark中用于图计算的API,性能好,拥有丰富的功能和运算符,能在海量数据上运行复杂的图算法。

  6. 集群管理器

    Spark 设计为可以高效地在一个计算节点到数千个计算节点之间伸缩计算。

(五)Spark运行模式
  • local本地模式(单机) —> 开发测试使用
  • standalone独立集群模式 —> 开发测试使用
  • standalone-HA高可用模式 —> 生产环境使用
  • on yarn集群模式 —> 生产环境使用

二、Spark环境搭建(掌握)

​ 我们选择目前企业中使用最多的是2017年07月发布的Spark2.2.0稳定版, 下载地址spark官网:http://spark.apache.org/downloads.html

  • 解压目录说明:
    • bin 可执行脚本
    • conf 配置文件
    • data 示例程序使用数据
    • examples 示例程序
    • jars 依赖 jar 包
    • LICENSE licenses
    • python pythonAPI
    • R R 语言 API
    • sbin 集群管理命令
    • yarn 整合yarn需要的东西
(一)local本地模式

集群启动

#启动进入
cd /export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/
#使用当前机器上所有可用的资源(可简写为bin/spark-shell)
bin/spark-shell --master local[*]

#或者	bin/spark-shell --master spark://node01:7077

体验: 读取本地文件

val textFile = sc.textFile("file:///export/words.txt")
val counts = textFile.flatMap(line=>line.split(" ")).map(word=>(word,1)).reduceByKey((a,b)=>a+b)
counts.collect//收集结果

体验: 读取HDFS文件

sc.textFile("hdfs://node01:8020/spark/day01/words.txt").flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _).saveAsTextFile("hdfs://node01:8020/spark/day01/output1")

(二)standalone集群模式

修改spark-env.sh文件

cd /export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/conf/
mv spark-env.sh.template spark-env.sh
vim  spark-env.sh 

#在文件后追加   配置java环境变量
export JAVA_HOME=/export/servers/jdk1.8.0_141
#指定spark老大Master的IP
export SPARK_MASTER_HOST=node01
#指定spark老大Master的端口
export SPARK_MASTER_PORT=7077

修改slaves文件(配置从节点worker)

mv slaves.template slaves
vim  slaves 

#删掉localhost,并在文件内后追加
node02
node03

通过scp 命令将配置文件分发到其他机器上

cd /export/servers/
scp -r spark-2.2.0-bin-2.6.0-cdh5.14.0 node02:$PWD
scp -r spark-2.2.0-bin-2.6.0-cdh5.14.0 node03:$PWD

#在node01主节点上启动spark集群
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/sbin/start-all.sh

#在node01主节点上停止spark集群
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/sbin/stop-all.sh
  • 查看spark的web界面: http://node01:8080

测试:

#集群模式启动spark-shell	SparkContext页面: http://192.168.1.101:4040
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/bin/spark-shell --master spark://node01:7077

#运行程序
sc.textFile("hdfs://node01:8020/spark/day01/words.txt").flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _).saveAsTextFile("hdfs://node01:8020/spark/day01/output3")

注意:
    集群模式下程序是在集群上运行的,读取hdfs上的上的文件, 不能直接读取本地文件。因为程序运行在集群上,其他节点可能并没有那个数据文件。


(三)standalone-HA模式

修改spark-env.sh文件

cd /export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/conf/
vim  spark-env.sh

#注释掉Master配置
#export SPARK_MASTER_HOST=node01

#添加SPARK_DAEMON_JAVA_OPTS, 在文件后追加
export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER  -Dspark.deploy.zookeeper.url=node01:2181,node02:2181,node03:2181  -Dspark.deploy.zookeeper.dir=/spark"

参数说明:

1. spark.deploy.recoveryMode:恢复模式(Master重新启动的模式)有三种:
	(1)ZooKeeper	(2) FileSystem	(3)NONE
2. spark.deploy.zookeeper.url:ZooKeeper的Server地址
3. spark.deploy.zookeeper.dir:保存集群元数据信息的文件、目录。包括Worker、Driver、Application信息。

通过scp 命令将配置文件分发到其他机器上

cd /export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/conf/
scp -r spark-env.sh  node02:$PWD
scp -r spark-env.sh  node03:$PWD

启动集群

#在node01上启动Spark集群
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/sbin/start-all.sh

#在node02上再单独只起个master:
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/sbin/start-master.sh

测试一:

#集群模式启动spark-shell
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/bin/spark-shell --master spark://node01:7077,node02:7077

#运行程序
sc.textFile("hdfs://node01:8020/spark/day01/words.txt").flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _).saveAsTextFile("hdfs://node01:8020/spark/day01/output6")

(四)on yarn集群模式

注意:

    把Spark程序提交给YARN运行本质是把字节码给YARN集群上的JVM运行, 但是要有一个东西帮我去把任务提交给YARN, 所以需要一个单机版的Spark,使用里面的spark-shell命令,spark-submit命令。

    on yarn集群模式启动zookeeper和Hadoop集群即可, 不需启动Spark集群

修改spark-env.sh文件

cd /export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/conf/
vim  spark-env.sh

#在文件后追加, 添加HADOOP_CONF_DIR配置,指明hadoop的配置文件的位置
export HADOOP_CONF_DIR=/export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop

配置历史日志服务器

#修改文件名
cd /export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/conf/
cp spark-defaults.conf.template spark-defaults.conf
vim spark-defaults.conf

#修改spark-defaults.conf文件
spark.eventLog.enabled true
spark.eventLog.dir hdfs://node01:8020/user/sparklog

#hdfs上的目录需要手动先创建
hdfs dfs -mkdir -p /user/sparklog/

#修改spark-env.sh文件
vim spark-env.sh

#追加以下内容
export SPARK_HISTORY_OPTS="-Dspark.history.ui.port=4000 -Dspark.history.retainedApplications=3 -Dspark.history.fs.logDirectory=hdfs://node01:8020/user/sparklog"

#通过scp 命令将配置文件分发到其他机器上
scp -r spark-env.sh  node02:$PWD
scp -r spark-defaults.conf  node02:$PWD
scp -r spark-env.sh  node03:$PWD
scp -r spark-defaults.conf  node03:$PWD

#重启集群
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/sbin/stop-all.sh
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/sbin/start-all.sh 

#在master上启动日志服务器      http://node01:4000/
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/sbin/start-history-server.sh

#关闭日志服务器
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/sbin/stop-history-server.sh

测试一: cluster模式

    Spark On YARN的Cluster模式指的是Driver程序运行在YARN集群上, 应用的运行结果不能在客户端显示, 运行应用程序的main()函数并创建SparkContext的进程, 在企业生产环境中更多都是cluster部署模式运行Spark 应用

#使用spark-submit提交打成jar包的任务	http://node01:8088/cluster
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
--executor-cores 2 \
--queue default \
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/examples/jars/spark-examples_2.11-2.2.0.jar \
10

测试二: client模式(了解)

    Spark On YARN的Client模式 指的是Driver程序运行在提交任务的客户端, 应用程序运行结果会在客户端显示, 学习测试时使用,开发不用,了解即可,可以略过

/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode client \
--driver-memory 1g \
--executor-memory 1g \
--executor-cores 2 \
--queue default \
/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/examples/jars/spark-examples_2.11-2.2.0.jar \
10

(五)Spark命令详解(了解)
  • spark-shell是Spark自带的交互式Shell程序,方便用户进行交互式编程,用户可以在该命令行下可以用scala编写spark程序,适合学习测试时使用!

    #spark-shell可以携带参数
    spark-shell --master local[N] #数字N表示在本地模拟N个线程来运行当前任务
    
    # *表示使用当前机器上所有可用的资源(默认不携带参数就是--master local[*])
    spark-shell --master local[*] 
    
    #如果搭建了HA, Master地址可以指定多个
    spark-shell --master spark://node01:7077,node02:7077
    
  • spark-submit命令用来帮我们提交jar包给spark集群/YARN去执行, 实际开发中常用

    案例: 蒙特·卡罗算法求圆周率PI

    #提交到spark集群的node01和node02上的master, 要开启Spark的standalone-HA模式
    /export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/bin/spark-submit \
    --class org.apache.spark.examples.SparkPi \
    --master spark://node01:7077,node02:7077 \
    --executor-memory 1g \
    --total-executor-cores 2 \
    /export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/examples/jars/spark-examples_2.11-2.2.0.jar \
    10
    
  • 总结: Master的参数形式

    Master形式解释
    local本地以一个worker线程运行(例如非并行的情况).
    local[N]本地以K worker 线程 (理想情况下, N设置为你机器的CPU核数).
    local[*]本地以本机同样核数的线程运行.
    spark://HOST:PORT连接到指定的Spark standalone cluster master的端口 7077.
    mesos://HOST:PORT连接到指定的Mesos 集群, 默认端口 5050, 或使用ZK: mesos://zk://…
    yarn-client以client模式连接到YARN cluster.
    yarn-cluster以cluster模式连接到YARN cluster.

三、IDEA编写Spark程序

(一)pom.xml

创建Maven项目并补全目录、配置pom.xml

 <!-- 指定仓库位置,依次为aliyun、cloudera和jboss仓库 -->
    <repositories>
        <repository>
            <id>aliyun</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        </repository>
        <repository>
            <id>cloudera</id>
            <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
        </repository>
        <repository>
            <id>jboss</id>
            <url>http://repository.jboss.com/nexus/content/groups/public</url>
        </repository>
    </repositories>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <encoding>UTF-8</encoding>
        <scala.version>2.11.8</scala.version>
        <scala.compat.version>2.11</scala.compat.version>
        <hadoop.version>2.7.4</hadoop.version>
        <spark.version>2.2.0</spark.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </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-hive-thriftserver_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.spark</groupId>
             <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
             <version>${spark.version}</version>
         </dependency>-->
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql-kafka-0-10_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>
 
        <!--<dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.6.0-mr1-cdh5.14.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.2.0-cdh5.14.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>1.2.0-cdh5.14.0</version>
        </dependency>-->
 
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.7.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.typesafe</groupId>
            <artifactId>config</artifactId>
            <version>1.3.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
    </dependencies>
 
    <build>
        <sourceDirectory>src/main/scala</sourceDirectory>
        <!--<testSourceDirectory>src/test/scala</testSourceDirectory>-->
        <plugins>
            <!-- 指定编译java的插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
            </plugin>
            <!-- 指定编译scala的插件 -->
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                        <configuration>
                            <args>
                                <arg>-dependencyfile</arg>
                                <arg>${project.build.directory}/.scala_dependencies</arg>
                            </args>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
                <configuration>
                    <useFile>false</useFile>
                    <disableXmlReport>true</disableXmlReport>
                    <includes>
                        <include>**/*Test.*</include>
                        <include>**/*Suite.*</include>
                    </includes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass></mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
(二)本地运行
//单词计数统计
object WordCount {
/**
  * Author Devin Kim
  * Date 2019/8/4 15:30
  * Desc 演示使用Spark编写WordCount
  */
  def main(args: Array[String]): Unit = {
    //1.创建SparkContext
    val config = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc = new SparkContext(config)
sc.setLogLevel("WARN")
    //2.读取文件
    //A Resilient Distributed Dataset (RDD)弹性分布式数据集
    //可以简单理解为分布式的集合,但是spark对它做了很多的封装,
    //让程序员使用起来就像操作本地集合一样简单
    val fileRDD: RDD[String] = sc.textFile(""D:\\1_my_study_work\\spark_day01\\words.txt"")
    //3.处理数据
    //3.1对每一行按空切分并压平形成一个新的集合中装的一个个的单词
    //flatMap是对集合中的每一个元素进行操作,再进行压平
    val wordRDD: RDD[String] = fileRDD.flatMap(_.split(" "))
    //3.2每个单词记为1
    val wordAndOneRDD: RDD[(String, Int)] = wordRDD.map((_,1))
    //3.3根据key进行聚合,统计每个单词的数量
    //wordAndOneRDD.reduceByKey((a,b)=>a+b)
    //第一个_:之前累加的结果
    //第二个_:当前进来的数据
    val wordAndCount: RDD[(String, Int)] = wordAndOneRDD.reduceByKey(_+_)
    //4.收集结果
    val result: Array[(String, Int)] = wordAndCount.collect()
    result.foreach(println)
    sc.stop()
  }
}
(三)集群运行
/**
  * Desc 演示使用Spark编写WordCount
  */
object WordCount {
  def main(args: Array[String]): Unit = {
    //1.创建SparkContext
    val config = new SparkConf().setAppName("wc")//.setMaster("local[*]") 等下使用spark-submit提交需要指定master地址
    val sc = new SparkContext(config)
    sc.setLogLevel("WARN")
    //2.读取文件
    //A Resilient Distributed Dataset (RDD)弹性分布式数据集
    //可以简单理解为分布式的集合,但是spark对它做了很多的封装,
    //让程序员使用起来就像操作本地集合一样简单,这样大家就很happy了
    val fileRDD: RDD[String] = sc.textFile(args(0)) //文件输入路径
    //3.处理数据
    //3.1对每一行按空切分并压平形成一个新的集合中装的一个个的单词
    //flatMap是对集合中的每一个元素进行操作,再进行压平
    val wordRDD: RDD[String] = fileRDD.flatMap(_.split(" "))
    //3.2每个单词记为1
    val wordAndOneRDD: RDD[(String, Int)] = wordRDD.map((_,1))
    //3.3根据key进行聚合,统计每个单词的数量
    //wordAndOneRDD.reduceByKey((a,b)=>a+b)
    //第一个_:之前累加的结果
    //第二个_:当前进来的数据
    val wordAndCount: RDD[(String, Int)] = wordAndOneRDD.reduceByKey(_+_)
    wordAndCount.saveAsTextFile(args(1))//文件输出路径
   //4.收集结果
   //val result: Array[(String, Int)] = wordAndCount.collect()
   //result.foreach(println)
  }
}

打包后将jar包提交到集群上运行(集群不用启动)

/export/servers/spark-2.2.0-bin-2.6.0-cdh5.14.0/bin/spark-submit \
--class cn.baicdt.demo01.WordCount \
--master spark://node01:7077,node02:7077 \
--executor-memory 1g \
--total-executor-cores 2 \
/root/wc.jar \
hdfs://node01:8020/spark/day01/words.txt \
hdfs://node01:8020/spark/output6
(四)Java8版[了解]
public class WordCount_Java {
    public static void main(String[] args){
        SparkConf conf = new SparkConf().setAppName("wc").setMaster("local[*]");
        JavaSparkContext jsc = new JavaSparkContext(conf);
        JavaRDD<String> fileRDD = jsc.textFile("D:\\1_my_study_work\\spark_day01\\words.txt");
        JavaRDD<String> wordRDD = fileRDD.flatMap(s -> Arrays.asList(s.split(" ")).iterator());
        JavaPairRDD<String, Integer> wordAndOne = wordRDD.mapToPair(w -> new Tuple2<>(w, 1));
        JavaPairRDD<String, Integer> wordAndCount = wordAndOne.reduceByKey((a, b) -> a + b);
        //wordAndCount.collect().forEach(t->System.out.println(t));
        wordAndCount.collect().forEach(System.out::println);
        //函数式编程的核心思想:行为参数化!
    }
}

补充:

​ ① Java8中的函数式编程的语法, lambda表达式 (参数)->{函数体}

​ ② Scala中函数的本质是对象

​ ③ Java8中函数的本质可以理解为匿名内部类对象,即Java8中的函数本质也是对象

//案例
public class Test {
    public static void main(String[] args){
      new Thread(
         new Runnable() {
          @Override
          public void run() {
              System.out.println("java8");
          }
        }
      ).start();

      //接下来使用Java8的lambda表达式(函数式编程)
      new Thread(
              ()->System.out.println("java8")
      ).start();
    }
}

四.案例:网络日志分析

import org.apache.spark.rdd.RDD
import org.apache.spark.storage.StorageLevel
import org.apache.spark.{SparkConf, SparkContext}

//演示使用Spark统计网站常见运营指标
object WebLog_T {
  def main(args: Array[String]): Unit = {
    //1.创建SC
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")
    //2.加载数据
    val fileRDD: RDD[String] = sc.textFile("D:\\1_my_study_work\\spark_day03\\access.log")
    //3.处理数据每一行按空格切分
    val linesRDD: RDD[Array[String]] = fileRDD.map(_.split(" "))

    //频繁使用的RDD可以进行缓存或持久化
    linesRDD.persist(StorageLevel.MEMORY_AND_DISK)
    //设置CheckpointDir
    sc.setCheckpointDir("./ckp")//实际开发中写HDFS
    //将RDD进行Checkpoint
    linesRDD.checkpoint()
/*===================================统计指标===================================*/
    //4.1统计网站pv, page view 网站页面浏览量(访问一次算一次)
    val pv: Long = linesRDD.count()
    println("pv: " + pv)
    val pvAndCount: RDD[(String, Int)] = linesRDD.map(line => ("pv",1)).reduceByKey(_+_)
    pvAndCount.collect().foreach(println)

    //4.2统计网站uv, user view 独立用户访问量(一个用户算一次访问,可以使用ip/Sessionid)
    val ipRDD: RDD[String] = linesRDD.map(line => line(0))
    val uv: Long = ipRDD.distinct().count()
    println("uv: " + uv)

    //4.3统计网站用户来源topN, 即统计refurl,表示来自于哪里?
    val filteredLines: RDD[Array[String]] = linesRDD.filter(_.length > 10)//数据预处理
    val refurlRDD: RDD[String] = filteredLines.map(_(10))//取出refUrl
    val refurlAndOne: RDD[(String, Int)] = refurlRDD.map((_,1))//每个refurl记为1
    val refurlAndCount: RDD[(String, Int)] = refurlAndOne.reduceByKey(_+_)//按照key聚合
    /*  val top5: Array[(String, Int)] = refurlAndCount.top(5)
        注意:top默认按照key排序,我们需要按照value次数排序
        并且top是把数据拉回Driver再排序    */
    val result = refurlAndCount.sortBy(_._2,false)//按照次数降序/逆序
    val top5: Array[(String, Int)] = result.take(5)
    top5.foreach(println)

    sc.stop()
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值