Scala 从入门到实战
一、基础语法
Scala 是一种多范式编程语言,它支持面向对象编程和函数式编程,并且提供了丰富的数据类型和灵活的语法。以下是 Scala 中常用的数据类型及其常见的语法用法:
常用的数据类型
-
基本数据类型
- Int: 整数类型,通常是32位有符号整数。
- Long: 长整数类型,64位有符号整数。
- Float: 单精度浮点数。
- Double: 双精度浮点数。
- Boolean: 布尔类型,取值为
true
或false
。 - Char: 单个字符,用单引号括起来,如
'a'
。 - String: 字符串类型,用双引号括起来,如
"Hello, Scala"
。
-
复合数据类型
- Array: 数组,包含相同类型的元素。
- List: 链表,可以包含不同类型的元素。
- Tuple: 元组,可以包含多个不同类型的值,一旦创建就不能改变。
- Set: 集合,包含不重复元素的无序集合。
- Map: 映射表,包含键值对的集合。
-
特殊数据类型
- Option: 表示可能有或者没有值的容器类型。
- Either: 表示可能是一种类型或另一种类型的值。
-
函数相关
- Function: 函数类型,在 Scala 中函数是一等公民,可以像变量一样传递和使用。
- PartialFunction: 部分函数类型,定义了一个函数的子集,只对部分输入值定义了行为。
常用的语法
-
变量和常量定义
- 使用
var
定义可变变量,使用val
定义不可变常量。var x: Int = 10 val y: String = "Scala"
- 使用
-
函数定义
- 使用
def
关键字定义函数。def add(x: Int, y: Int): Int = x + y
- 使用
-
控制结构
- 条件表达式
val result = if (x > 10) "Greater" else "Smaller"
- 循环结构
for (i <- 1 to 5) { println(i) }
- 条件表达式
-
模式匹配
- 使用
match
关键字进行模式匹配。val day = 3 val dayType = day match { case 1 => "Monday" case 2 => "Tuesday" case _ => "Other day" }
- 使用
-
集合操作
- 数组
val numbers = Array(1, 2, 3, 4, 5)
- 列表
val fruits = List("apple", "banana", "orange")
- 映射
val ages = Map("Alice" -> 30, "Bob" -> 25, "Charlie" -> 35)
- 数组
-
函数式编程特性
- 高阶函数
def applyOperation(x: Int, f: Int => Int): Int = f(x)
- 匿名函数
val addOne = (x: Int) => x + 1
- 高阶函数
-
类和对象
- 类的定义
class Person(var name: String, var age: Int)
- 对象的定义
object MathUtils { def add(x: Int, y: Int): Int = x + y }
- 类的定义
Scala 的语法支持强大而灵活,结合了面向对象和函数式编程的特性,使得它在大数据处理、并发编程和领域特定语言等领域有广泛的应用。
二、spark和scala之间的关系
- Spark 和Hadoop 的根本差异是多个作业之间的数据通信问题 : Spark 多个作业之间数据通信是基于内存,而 Hadoop 是基于磁盘。
- Spark本身就是使用Scala语言开发的,spark和flink的底层通讯都是基于的高并发架构akka开发,然而akka是用scala开发的,Scala与Spark可以实现无缝结合,因此Scala顺理成章地成为了开发Spark应用的首选语言
- 在IDEA中开发Spark,可以使用两种方式环境方式:
①、一是使用本地Scala库,建立Scala项目,导入Spark jar包。
②、一种是通过Maven引入Scala、Spark依赖。我们本次使用Maven的方式,符合Java开发者的习惯于行业规范。
先简单了解下scala的语言特性和项目建立:
- Scala源文件以 “.scala" 为扩展名。
- Scala程序的执行入口是main()函数。
- Scala语言严格区分大小写。
- Scala方法由一条条语句构成,每个语句后不需要分号(Scala语言会在每行后自动加分号),这也体现出Scala的简洁性。
- 如果在同一行有多条语句,除了最后一条语句不需要分号,其它语句需要分号
object 关键字解读
在Scala中,object
关键字用于定义一个单例对象。它和Java中的 class
或 interface
有显著的不同。以下是 object
关键字在Scala中的用法及其与Java的主要区别:
1. 单例对象(Singleton Object)
在Scala中,object
定义了一个类的单例对象。这意味着每次访问这个对象时,都是访问同一个实例。Scala的object
可以看作是一个静态成员的容器,但它不仅仅是静态的,也可以拥有方法、字段和初始化代码。
语法示例
object MySingleton {
def greet(name: String): Unit = {
println(s"Hello, $name!")
}
}
使用示例
MySingleton.greet("Alice") // 输出: Hello, Alice!
2. 伴生对象(Companion Object)
在Scala中,object
也常用于伴生对象。伴生对象是与某个类同名的object
,它和类存在于同一个源文件中。伴生对象可以访问类的私有成员,并且可以用于实现工厂方法或静态方法。
类和伴生对象的定义示例
class Person(val name: String)
object Person {
def createPerson(name: String): Person = new Person(name)
}
使用示例
val person = Person.createPerson("Bob")
println(person.name) // 输出: Bob
3. 与Java的区别
在Java中,所有的类和对象都是实例化的,Java没有直接对应的单例模式机制(除了static
关键字提供的静态成员)。Scala的object
关键字提供了更为简洁和强大的方式来实现单例模式。
Java单例模式示例
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
Scala单例对象示例
Scala中的object
直接创建了一个单例对象,简化了代码:
object Singleton {
def doSomething(): Unit = {
println("Doing something...")
}
}
4. 静态成员
在Java中,可以使用static
关键字来定义静态成员,这些成员属于类而不是类的实例。Scala的object
可以看作是一个容纳静态成员的容器,所有在object
内部定义的成员都是静态的。
Java静态成员示例
public class MyClass {
public static int staticValue = 10;
public static void staticMethod() {
System.out.println("Static method.");
}
}
Scala对象中的成员示例
object MyClass {
val staticValue = 10
def staticMethod(): Unit = {
println("Static method.")
}
}
总结
- Scala的
object
:用于定义单例对象或伴生对象,简化了单例模式的实现,允许定义静态成员和方法。 - Java的
static
:用于定义静态成员和方法,但没有单例对象的直接概念,需要通过额外的设计模式来实现单例模式。
通过Scala的object
,可以更自然地实现单例模式并进行更为简洁的代码组织。
提交scala任务到spark
将Scala代码提交到Spark集群进行任务运行通常涉及以下几个步骤。这个过程包括编译代码、打包应用程序、配置Spark环境,以及最终通过spark-submit
命令提交任务到集群。下面是详细的步骤:
1. 编写和测试代码
首先,你需要在本地开发环境中编写和测试你的Spark应用程序。假设你已经完成了这部分。
2. 打包Scala应用程序
将你的Scala应用程序打包成一个可执行的JAR文件。这个步骤通常使用构建工具如SBT
或Maven
来完成。
使用SBT
-
创建
build.sbt
文件:name := "MySparkApp" version := "1.0" scalaVersion := "2.12.17" // 确保版本与你的Spark版本兼容 libraryDependencies ++= Seq( "org.apache.spark" %% "spark-core" % "3.4.0", "org.apache.spark" %% "spark-sql" % "3.4.0", "org.apache.spark" %% "spark-hive" % "3.4.0" ) assemblyOption in Assembly := (assemblyOption in Assembly).value.copy(includeScala = false) mainClass in assembly := Some("com.example.SparkApp")
-
创建
project/plugins.sbt
文件(如果使用SBT assembly插件来打包):addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.0")
-
打包应用程序:
在项目根目录下运行:
sbt clean assembly
这将生成一个包含所有依赖项的JAR文件,通常位于
target/scala-2.12/
目录下。
使用Maven
-
配置
pom.xml
:<dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.12</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.12</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-hive_2.12</artifactId> <version>3.4.0</version> </dependency> </dependencies> <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> <createDependencyReducedPom>false</createDependencyReducedPom> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> </configuration> </execution> </executions> </plugin> </plugins> </build>
-
打包应用程序:
在项目根目录下运行:
mvn clean package
生成的JAR文件通常位于
target/
目录下。
3. 配置Spark环境
确保你的Spark集群已正确配置,并且能够访问Hive(如果你使用Hive)。你需要确保以下配置:
-
Spark配置:
spark-defaults.conf
配置文件,包含Spark的基本配置。spark-env.sh
配置文件,设置环境变量(如Spark的路径)。
-
Hive配置(如果使用Hive):
- 确保
hive-site.xml
文件位于Spark的conf
目录下,或者你可以将其放在你的JAR文件的资源目录中。
- 确保
4. 提交任务到Spark集群
使用 spark-submit
命令将你的应用程序提交到Spark集群。
-
基本的
spark-submit
命令:spark-submit \ --class com.example.SparkApp \ --master yarn \ --deploy-mode cluster \ --conf spark.sql.warehouse.dir=/user/hive/warehouse \ path/to/your-application.jar
--class
:指定应用程序的主类。--master
:指定Spark集群的管理模式(如yarn
、standalone
)。--deploy-mode
:指定部署模式(client
或cluster
)。--conf
:传递Spark配置参数。path/to/your-application.jar
:JAR文件的路径。
-
其他常用选项:
--executor-memory
:指定每个executor的内存大小。--executor-cores
:指定每个executor的核心数量。--num-executors
:指定executor的数量。
例如:
spark-submit \ --class com.example.SparkApp \ --master yarn \ --deploy-mode cluster \ --conf spark.sql.warehouse.dir=/user/hive/warehouse \ --executor-memory 4G \ --executor-cores 2 \ --num-executors 10 \ path/to/your-application.jar
5. 监控和调试
-
查看应用程序日志:
- 在Spark Web UI中查看应用程序的日志和执行状态。Spark Web UI通常位于
http://<spark-master-node>:4040
。
- 在Spark Web UI中查看应用程序的日志和执行状态。Spark Web UI通常位于
-
调试应用程序:
- 使用日志文件和Spark Web UI中的信息来调试和优化应用程序。
6. 清理资源
完成任务后,确保释放集群资源,特别是当你在cluster
模式下运行时,以免不必要的资源占用。
以上步骤提供了从开发到部署的完整流程。根据实际的集群配置和应用需求,你可能需要做一些调整。
参数解释
这些参数用于配置Spark作业的资源分配:
--executor-memory 4G
:为每个executor分配4GB的内存。--executor-cores 2
:每个executor使用2个核心进行计算。--num-executors 10
:总共启动10个executor进行任务处理。
这些设置帮助优化作业性能,根据集群资源的情况进行调整。
在Apache Spark中,executor
是一个重要的概念,它是Spark集群中执行任务的工作进程。以下是关于executor
的详细解释:
Executor的功能
-
任务执行:
executor
负责执行Spark作业中的任务。每个executor
可以并行地处理多个任务,这些任务来自Spark作业的不同分区。
-
数据存储:
executor
不仅执行计算任务,还负责存储数据。它会在内存中缓存数据,以便在后续的操作中可以更快地访问这些数据。这个缓存机制是Spark的一个关键特性,可以显著提高性能。
-
结果汇总:
- 执行任务的结果最终会汇总到
executor
中,并通过驱动程序(Driver)进行处理和输出。executor
向驱动程序报告任务状态和计算结果。
- 执行任务的结果最终会汇总到
Executor的配置参数
-
内存(
--executor-memory
):- 为每个
executor
分配的内存量。例如,--executor-memory 4G
表示每个executor
有4GB的内存。这个内存用于存储数据和执行任务。
- 为每个
-
核心数(
--executor-cores
):- 每个
executor
分配的计算核心数量。例如,--executor-cores 2
表示每个executor
使用2个计算核心。核心数决定了每个executor
可以并行处理多少任务。
- 每个
-
num-executors
:- 总共启动的
executor
数量。例如,--num-executors 10
表示启动10个executor
。executor
的数量影响了作业的并行度和总体计算能力。
- 总共启动的
Executor的生命周期
executor
是一个长期运行的进程,在整个Spark作业的生命周期内保持活跃。它通常在集群中启动一次,并在作业完成后继续运行,直到集群的资源被释放或作业被结束。
总结
executor
是Spark计算框架中的工作单元,负责具体的任务执行、数据存储和结果汇总。合理配置executor
的内存、核心数和数量,对于优化Spark作业的性能至关重要。
实践
package com.spark
/**
* object 单例对象中不可以传参,
* 如果在创建Object时传入参数,那么会自动根据参数的个数去Object中寻找相应的apply方法
*/
object TestLesson {
// object 相当于Java 的工具类
def apply(s: String) = {
println("name is " + s)
}
def apply(s:String, age: Int) = {
println("name is " + s + ",age=" + age)
}
def main(args: Array[String]): Unit = {
TestLesson("kaiyi")
TestLesson("kaiyi", 18)
}
}
说明:apply方法在Scala中不是初始化对象的构造函数。它是一个特定的方法
,用于提供一种简洁的对象创建方式。它可以定义在object、class或companion object中,而不仅限于构造函数。apply方法可以有多个重载形式,接受不同数量和类型的参数,并且不一定返回对象的实例。
- 当你调用TestLesson("kaiyi")时,Scala会自动将其转换为TestLesson.apply("kaiyi")。这会触发TestLesson对象中的apply(s: String)方法,并输出name is kaiyi。
- 当你调用TestLesson("kaiyi", 18)时,Scala会转换为TestLesson.apply("kaiyi", 18),触发TestLesson对象中的apply(s: String, age: Int)方法,并输出name is kaiyi, age=18。
相关文章:
【Spark】scala基础入门
为者常成,行者常至
自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)