3.使用spark开发第一个程序WordCount程序及多方式运行代码

本文章已经生成可运行项目,

概述

WordCount 是一个快速入门案例,单词统计,通过此案例,学习如何用 scala 来编写 spark 程序,spark 支持 javascalal 这些语言,目前在企业中大部分公司都是使用 scala 进行开发,后序的 flink 是基于 java 开发的,这与官网的引导有关,flink 的源码在去scala 化 ,基于此,将要实现以下几个目标:

  • WordCount程序
  • 任务提交
    • 使用 idea
    • 使用 spark-submit
    • 使用 spark-shell
  • Spark historyServer 配置

前置文章请参考:

文章地址
scala安装地址
idea如何开发spark代码地址
Spark的工作与架构原理地址
RDD编程指南地址
RDD持久化地址
Spark共享变量地址

此篇文章涉及到相关的知识点较多,跟着做完,即能有所得

WordCount程序

本程序 基于 spark 3.2.4scala 2.12.x 版本开发

准备工作

由于后续任务运行时,需要在 客户端节点或hadoop节点机器上 操作,文件来源,使用 hdfs
hello.txt上传至 hdfs如下操作

[root@hadoop01 data]# hdfs dfs -put hello.txt /tmp/
2023-11-02 09:23:45,159 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[root@hadoop01 data]# ls
hadoop_repo  hello.txt  hive_repo  soft

上传结果如下图
在这里插入图片描述

scala编写代码

编码之前,要清楚代码的逻辑,即加载的数据如何一步一步变换成最终想要的结果 ,由下分析步骤可知,万变不离其宗 S -> T -> S 三板斧, 详细请参考 Spark的工作与架构原理

  • 创建SparkContext
  • 加载数据(S)
  • 对数据进行分隔,将一行数据分隔成一个一个的单词 (T)
  • 迭代 words,将第个 word 转化为 (word,1) 这种形式 (T)
  • 第五步:根据 key (其实就是 word) 进行分组聚合统计 (T)
  • 第六步:结果打印 (S)

代码如下

object WordCount {
  def main(args: Array[String]): Unit = {
    // "hdfs:///tmp/hello.txt"
    var path = "/Users/hyl/Desktop/fun/sts/spark-demo/hello.txt"
    if(args.length==1){
      path = args(0)
    }

    // 第一步:创建SparkContext
    val conf = new SparkConf()
    conf
      // 设置任务名称
      .setAppName("WordCount")
      // local 表示本地运行
      .setMaster("local")
    val sp = new SparkContext(conf)
    // 第二步:加载数据
    val lineRdd = sp.textFile(path)
    // 第三步:对数据进行分隔,将一行数据分隔成一个一个的单词
    val wordsRdd = lineRdd.flatMap(_.split(" "))
    // 第四步:迭代 words,将第个 word 转化为 (word,1) 这种形式
    val pairRdd = wordsRdd.map((_, 1))
    // 第五步:根据 key (其实就是 word) 进行分组聚合统计
    val wordCountRdd = pairRdd.reduceByKey(_ + _)
    // 第六步:结果打印
    wordCountRdd.foreach(println _)
  }
}

任务提交

使用 idea

运行 idea 得如下结果
在这里插入图片描述
来体验一下 scala 函数式编程的极简之美

object WordCount2 {
  def main(args: Array[String]): Unit = {
    var path = "/Users/hyl/Desktop/fun/sts/spark-demo/hello.txt"
    if (args.length == 1) {
      path = args(0)
    }
    val conf = new SparkConf()
    conf
      // 设置任务名称
      .setAppName("WordCount")
      // local 表示本地运行
      .setMaster("local")
    new SparkContext(conf).textFile(path).flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _).foreach(println _)
  }
}

使用 spark-submit

使用 spark-submit 提交到集群执行,实际工作中会使用这种方式
那接下来需要把代码提交到集群中去执行
这个时候就需要对代码打包了
首先在项目的 pom文件中添加 build 配置,和 dependencies 标签平级,详细请参考 源码

注意: 打包时,要将如下图的代码注释掉

[root@hadoop01 jar]# ls
spark-demo-1.0-SNAPSHOT.jar
[root@hadoop01 jar]# pwd
/data/jar

spark-submit \
--class com.fun.scala.WordCount2 \
--master yarn \
--deploy-mode client \
--executor-memory 1G \
--num-executors 1 \
/data/jar/spark-demo-1.0-SNAPSHOT.jar \
hdfs:///tmp/hello.txt

异常

Exception in thread “main” org.apache.spark.SparkException: When running with master ‘yarn’ either HADOOP_CONF_DIR or YARN_CONF_DIR must be set in the environment.

在这里插入图片描述

[root@hadoop01 bin]# vi /etc/profile
export HADOOP_CONF_DIR=/data/soft/hadoop-3.2.4/etc/hadoop/
[root@hadoop01 bin]# source /etc/profile
[root@hadoop01 bin]# 

执行成功
在这里插入图片描述

使用 spark-shell

这种方式方便在集群环境中调试代码
有一些代码对环境没有特殊依赖的时候可以直接使用第一种方式,在idea中调试代码
但是有时候代码需要依赖线上的一些环境,例如:需要依赖线上的数据库中的数据,由于权限问题,在本地是无法连接的

这个时候想要调试代码的话,可以选择使用spark-shell的方式,直接在线上服务器中开启一个spark 的交互式命令行窗口

注意:使用spark-shell的时候,也可以选择指定开启本地spark集群,或者连接standalone集群,或者使用on yarn模式,都是可以的

[root@hadoop01 bin]# spark-shell 
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
2023-11-02 10:20:11,746 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Spark context Web UI available at http://hadoop01:4040
Spark context available as 'sc' (master = local[*], app id = local-1698891612526).
Spark session available as 'spark'.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 3.2.4
      /_/
         
Using Scala version 2.12.15 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_391)
Type in expressions to have them evaluated.
Type :help for more information.

scala> 

执行需要的代码

scala> val path = "hdfs:///tmp/hello.txt"
path: String = hdfs:///tmp/hello.txt

scala> sc.textFile(path).flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _).foreach(println _)
(hyl,1)0:>                                                          (0 + 2) / 2]
(word,1)
(hello,4)
(12755167,1)
(test,1)
                                                                                
scala> 

效果图
在这里插入图片描述

Spark historyServer 配置

请先配置 hadoop historyServer

刚才使用 on yarn 模式的时候会发现看不到输出的日志信息,这主要是因为没有开启 sparkhistoryserver,只开启了hadoophistoryserver
需要修改spark-defaults.confspark-env.sh
首先对spark-defaults.conf.template重命名
然后在spark-defaults.conf中增加以下内容

spark.eventLog.enabled=true
spark.eventLog.compress=true
spark.eventLog.dir=hdfs:///tmp/logs/root/logs
spark.history.fs.logDirectory=hdfs:///tmp/logs/root/logs
spark.yarn.historyServer.address=http://hadoop01:18080

注意:在哪个节点上启动spark的historyserver进程,spark.yarn.historyServer.address的值里面就指定哪个节点的主机名信息

spark-env.sh中增加以下内容

export SPARK_HISTORY_OPTS="-Dspark.history.ui.port=18080 -Dspark.history.fs.logDirectory=hdfs:///tmp/logs/root/logs"

bug

java.io.FileNotFoundException: File does not exist: hdfs:/tmp/logs/root/logs

在这里插入图片描述
解决 : 预先建立一个目录

[root@hadoop01 spark-3.2.4-bin-hadoop3.2]# hdfs dfs -mkdir hdfs:/tmp/logs/root/logs
2023-11-02 10:36:47,136 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[root@hadoop01 spark-3.2.4-bin-hadoop3.2]# sbin/start-history-server.sh
starting org.apache.spark.deploy.history.HistoryServer, logging to /data/soft/spark-3.2.4-bin-hadoop3.2/logs/spark-root-org.apache.spark.deploy.history.HistoryServer-1-hadoop01.out
[root@hadoop01 spark-3.2.4-bin-hadoop3.2]# jps
11716 NameNode
2628 Jps
12117 SecondaryNameNode
12503 ResourceManager
10520 Master
11243 RunJar
2333 HistoryServer

yarn 上重新执行 WorkCount 程序,可以看到以下输出内容
在这里插入图片描述

结束

至此 WordCount 程序开发及运行结束,如有问题,欢迎评论区留言。

本文已生成可运行项目
<think> 根据用户的问题,我们需要解决使用spark-submit提交WordCount应用时出现java.lang.ClassNotFoundException: WordCount的错误。 结合引用中给出的错误信息,我们可以分析出两个引用都是关于ClassNotFoundException的,但引用[1]是关于Spark SQL CLI缺少Hive支持,引用[2]是关于提交WordCount应用时找不到主类。 根据引用[2]的错误信息: java.lang.ClassNotFoundException: com.huijie.bigdata.spark.WorldCount 这通常是因为在提交应用时,Spark找不到指定的类。可能的原因和解决方法如下: 1. **JAR包中未包含主类**:提交的JAR文件没有包含指定的主类(com.huijie.bigdata.spark.WorldCount)。需要确认打包时是否包含了该类,并且路径正确。 - 解决方案:检查打包过程,确保主类被打包进JAR文件。例如,如果使用Maven,确保在pom.xml中正确配置了主类,并且使用mvn assembly:assembly或mvn package打包。 2. **类名错误**:提交时指定的类名(--class)与实际的类名不一致。注意大小写和包路径。 - 解决方案:检查实际类名,确保在spark-submit命令中指定的类名完全一致(包括包名)。 3. **JAR文件路径错误**:在spark-submit命令中指定的JAR文件路径不正确,或者该JAR文件不包含所需的类。 - 解决方案:检查JAR文件路径是否正确,并确认该JAR文件包含主类。可以使用以下命令检查JAR文件内容: jar tf WordCount-jar-with-dependencies.jar | grep WorldCount 如果找不到,说明打包有问题。 4. **依赖问题**:如果使用了多个JAR文件,可能需要通过--jars参数指定依赖的JAR,或者将所有依赖打包到一个JAR中(uber jar)。 - 解决方案:确保所有依赖都被正确打包,或者使用--jars参数指定额外的JAR文件。 5. **集群部署问题**:在集群模式下,需要确保JAR文件在所有工作节点上可用。如果使用本地文件路径,则应该使用--deploy-mode client,并且文件在客户端节点上;如果使用HDFS路径,则应该上传到HDFS。 - 解决方案:将JAR文件上传到HDFS,然后在spark-submit命令中使用HDFS路径。 根据引用[2]中的命令: bin/spark-submit --class com.huijie.bigdata.spark.WorldCount WordCount-jar-with-dependencies.jar 错误的原因是找不到类com.huijie.bigdata.spark.WorldCount,因此我们应该优先检查上述第1、2、3点。 具体步骤: 1. 确认类名:在IDE中检查主类的完整名称(包括包名),确保与--class参数一致。 2. 检查JAR文件内容:使用jar tf命令查看JAR包内的类文件路径。 3. 重新打包:如果发现类不在JAR中,需要重新正确打包。 如果用户使用Maven,可以尝试使用maven-shade-plugin来构建包含所有依赖的JAR文件,并指定主类。 示例pom.xml配置: <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.huijie.bigdata.spark.WorldCount</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> 打包命令:mvn clean package 然后使用生成的JAR文件(通常在target目录下)提交。 另外,如果用户使用sbt,可以使用sbt-assembly插件。 如果问题仍然存在,检查类名是否拼写错误(注意WorldCount和WordCount的区别?引用[2]中类名是WorldCount,而用户的问题中写的是WordCount,这可能是个笔误)。 在用户的问题描述中,错误是java.lang.ClassNotFoundException: WordCount,而引用[2]中错误是java.lang.ClassNotFoundException: com.huijie.bigdata.spark.WorldCount。这里用户的问题中类名是WordCount(没有包名?),而引用[2]中有完整的包名。因此,用户可能没有指定完整的包名。 总结解决方案: - 确保spark-submit命令中--class参数指定的是完整的类名(包括包名)。 - 确保JAR文件包含该类并且路径正确。 - 检查打包过程是否正确。 另外,如果用户在本地运行,确保没有多个不同版本的JAR文件冲突。 因此,针对用户的问题,我们可以给出以下步骤: 1. 检查spark-submit命令中的--class参数,确保类名正确且包含完整的包名。 2. 检查JAR文件是否包含该类(使用jar tf查看)。 3. 重新正确打包应用,生成包含所有依赖的JAR文件(uber jar)。 4. 如果使用集群模式,确保JAR文件在集群上可访问(如上传到HDFS)。 注意:用户错误信息显示的是WordCount(没有包名),所以很可能用户没有写完整的包名。例如,如果完整的类名是com.example.WordCount,那么在spark-submit中应该写--class com.example.WordCount。 因此,用户应该检查其主类的包名,并在提交命令中指定完整的类名。 如果用户在命令中已经指定了完整的类名,那么问题可能出在JAR文件打包上。 </think> ### 解决 Spark-Submit 提交 WordCount 应用时出现 `ClassNotFoundException` 的错误 该错误通常由以下原因引起,以下是具体排查步骤和解决方案: #### 1. **检查类名拼写和路径(最常见原因)** - **问题**:`--class` 参数指定的类名与代码中的实际类名**不一致**(大小写、包路径错误) - **解决方案**: - 确认主类全限定名(包名+类名),例如实际类名应为:`--class com.example.WordCount` - 执行命令: ```bash spark-submit --class [完整包路径].WordCount [JAR路径] ``` #### 2. **验证 JAR 文件是否包含主类** - **问题**:打包时未包含主类或打包配置错误 - **解决方案**: 1. 检查 JAR 内容: ```bash jar tf WordCount.jar | grep "WordCount.class" ``` 2. 若未找到,需重新打包: - Maven 项目:确保 `pom.xml` 包含主类声明: ```xml <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>[完整包路径].WordCount</mainClass> </transformer> </transformers> </configuration> </plugin> ``` - 重新打包:`mvn clean package` #### 3. **核对 JAR 文件路径** - **问题**:提交命令中的 JAR 路径错误 - **解决方案**: - 使用绝对路径: ```bash spark-submit --class com.example.WordCount /opt/spark/jars/WordCount.jar ``` - 或上传至 HDFS 后引用: ```bash spark-submit --class com.example.WordCount hdfs:///jobs/WordCount.jar ``` #### 4. **检查依赖项打包** - **问题**:未打包依赖库(第三方 JAR) - **解决方案**: - 构建包含依赖的 FAT JAR: - Maven: 使用 `maven-assembly-plugin` 或 `maven-shade-plugin` - SBT: 添加 `assembly` 插件 - 或通过 `--jars` 附加依赖: ```bash spark-submit --jars lib1.jar,lib2.jar --class ... WordCount.jar ``` #### 5. **集群部署模式问题** - **问题**:集群模式下 JAR 文件未分发到工作节点 - **解决方案**: - 上传 JAR 到集群可见存储(如 HDFS) - 使用 `--deploy-mode cluster` 时需确保主类在 Driver 节点可访问 #### 完整修复示例 ```bash # 步骤 1: 确认类名(假设包名为 com.data) grep "public class WordCount" src/main/java/com/data/WordCount.java # 步骤 2: 重新打包 mvn clean package # 步骤 3: 提交(使用绝对路径) spark-submit \ --class com.data.WordCount \ /home/user/spark-job/wordcount-1.0.jar ``` > **关键提示**: > - 错误日志 `ClassNotFoundException: WordCount` 中的类名**必须包含完整包路径** > - 90% 的此类错误由类名拼写错误或打包配置问题导致[^1][^2] --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流月up

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值