实验⼗四Spark实验:SparkWordCount
实验指导:
14.1 实验⽬的
熟悉Scala语⾔,基于Spark思想,编写SparkWordCount程序。
14.2 实验要求
熟悉Scala语⾔,理解Spark编程思想,并会编写Spark 版本的WordCount,然后能够在spark-shell中执⾏代码和分析执⾏过程。14.3 实验原理
Scala 是⼀门以 Java 虚拟机(JVM)为⽬标运⾏环境并将⾯向对象 (OO) 和函数式编程语⾔ (FP) 的最佳特性结合在⼀起的编程语⾔。
它既有动态语⾔那样的灵活简洁,同时⼜保留了静态类型检查带来的安全保障和执⾏效率,加上其强⼤的抽象能⼒,既能处理脚本化的临时任务,⼜能处理⾼并发场景下的分布式互联⽹⼤数据应⽤,可谓能缩能伸。
Scala 运⾏在JVM 之上,因此它可以访问任何 Java 类库并且与 Java 框架进⾏互操作。其与Java的集成度很⾼,可以直接使⽤ Java 社区⼤量成熟的技术框架和⽅案。由于它直接编译成Java字节码,因此我们可以充分利⽤JVM这个⾼性能的运⾏平台。
14.3.1 Scala是兼容的
Scala被设计成⽆缝地与Java实施互操作。不需要你从Java平台后退两步然后跳到Java语⾔前⾯去。它允许你在现存代码中加点⼉东西——在你已有的东西上建设,Scala程序会被编译为JVM的字节码。它们的执⾏期性能通常与Java程序⼀致。Scala代码可以调⽤Java⽅法,访问Java字段,继承⾃Java类和实现Java接⼝。这些都不需要特别的语法,显式接⼝描述,或粘接代码。实际上,⼏乎所有Scala代码都极度依赖于Java库,⽽程序员⽆须意识到这点 。
交互式操作的另⼀个⽅⾯是Scala极度重⽤了Java类型 。Scala的Int类型代表了Java的原始整数类型int,Float代表了float,Boolean代表boolean,等等。Scala的数组被映射到Java数组。Scala同样重⽤了许多标准Java库类型。例如,Scala⾥的字串⽂本”abc”是
java.lang.String,⽽抛出的异常必须是java.lang.Throwable的⼦类。
Scala不仅重⽤了Java的类型,还把它们“打扮”得更漂亮 。例如,Scala的字串⽀持类似于toInt和toFlo
at的⽅法,可以把字串转换成整数或者浮点数。因此你可以写Int替代Integer.parseInt(str)。如何在不打破互操作性的基础上做到这点呢?Java的String类当然不会有toInt⽅法。实际上,Scala有⼀个解决这种⾼级库设计和互操作性不相和谐的通⽤⽅案。Scala可以让你定义隐式转换:implicit conversion,这常常⽤在类型失配,或者选⽤不存在的⽅法时。在上⾯的例⼦⾥,当在字串中寻toInt⽅法时,Scala编译器会发现String类⾥没有这种⽅法,但它会发现⼀个把Java的String转换为Scala的RichString类的⼀个实例的隐式转换,⾥⾯定义了这么个⽅法。于是在执⾏toInt操作之前,转换被隐式应⽤ 。
Scala代码同样可以由Java代码调⽤ 。有时这种情况要更加微妙,因为Scala是⼀种⽐Java更丰富的语⾔,有些Scala更先进的特性在它们能映射到Java前需要先被编码⼀下。
14.3.2 Scala是简洁的
Scala程序⼀般都很短 。Scala程序员曾报告说与Java⽐起来代码⾏数可以减少到1/10。这有可能是个极限的例⼦。较保守的估计⼤概标准的Scala程序应该有Java写的同样的程序⼀半⾏数左右。更少的⾏数不仅意味着更少的打字⼯作,同样意味着更少的话在阅读和理解程序上的努⼒及更少的出错可能。许多因素在减少代码⾏上起了作⽤ 。Scala的语法避免了⼀些束缚Java程序的固定写法 。例如,Scala⾥的分号是可选的,且通常不写。Scala语法⾥还有很多其他的地⽅省略了东西。⽐⽅说,⽐较⼀下你在Java和Scala⾥是如何写类及构造函数的。
在Java⾥,带有构造函数的类经常看上去是这个样⼦:
class MyClass {
private int index;
private String name;
public MyClass(int index, String name) {
this.index = index;
this.name = name;
}
}
在Scala⾥,你会写成这样:
class MyClass(index: Int, name: String)
根据这段代码,Scala编译器将制造有两个私有成员变量的类,⼀个名为index的Int类型和⼀个叫做name的String类型,还有⼀个⽤这些变量作为参数获得初始值的构造函数。这个构造函数还将⽤作为参数传⼊的值初始化这两个成员变量。⼀句话,你实际拿到了与罗嗦得多的Java版本同样的功能。Scala类写起来更快,读起来更容易,最重要的是,⽐Java类更不容易犯错。
有助于Scala的简洁易懂的另⼀个因素是它的类型推断 。重复的类型信息可以被忽略,因此程序变得更有条理和易读,但或许减少代码最关键的是因为已经存在于你的库⾥⽽不需要写的代码。Scala给了你许多⼯具来定义强有⼒的库让你抓住并提炼出通⽤的⾏为。例如,库类的不同⽅⾯可以被分成若⼲特质,⽽这些⼜可以被灵活地混合在⼀起。或者,库⽅法可以⽤操作符参数化,从⽽让你有效地定义那些你⾃⼰控制的构造。这些构造组合在⼀起,就能够让库的定义既是⾼层级的⼜能灵活运⽤。
14.3.3 Scala是⾼级的
程序员总是在和复杂性纠缠 。为了⾼产出的编程,你必须明⽩你⼯作的代码。过度复杂的代码成了很多软件⼯程崩溃的原因。不幸的是,重要的软件往往有复杂的需求。这种复杂性不可避免;必须(由不受控)转为受控。
Scala可以通过让你提升你设计和使⽤的接⼝的抽象级别来帮助你管理复杂性 。例如,假设你有⼀个String变量name,你想弄清楚是否String包含⼀个⼤写字符。
在Java⾥,你或许这么写:
boolean nameHasUpperCase = false;
for (int i = 0; i > name.length(); ++i) {
if (Character.isUpperCase(name.charAt(i))) {
nameHasUpperCase = true;
break;scala不是内部或外部命令
}
}
在Scala⾥,你可以写成:
val nameHasUpperCase = ists(_.isUpperCase)
Java代码把字符串看作循环中逐字符步进的低层级实体。Scala代码把同样的字串当作能⽤论断:pred
icate查询的字符⾼层级序列 。明显Scala代码更短并且——对训练有素的眼睛来说——⽐Java代码更容易懂。因此Scala代码在通盘复杂度预算上能极度地变轻,它也更少给你机会犯错 。
论断,_.isUpperCase,是⼀个Scala⾥⾯函数式⽂本的例⼦ 。它描述了带⼀个字符参量(⽤下划线字符代表)的函数,并测试其是否为⼤写字母 。原则上,这种控制的抽象在Java中也是可能的 ,为此需要定义⼀个包含抽象功能的⽅法的接⼝。例如,如果你想⽀持对字串的查询,就应引⼊⼀个只有⼀个⽅法hasProperty的接⼝CharacterProperty:
interface CharacterProperty {
boolean hasProperty(char ch);
}
然后你可以在Java⾥⽤这个接⼝格式⼀个⽅法exists:它带⼀个字串和⼀个CharacterProperty并返回真如果字串中有某个字符符合属性。然后你可以这样调⽤exists:
exists(name, new CharacterProperty {
boolean hasProperty(char ch) {
return Character.isUpperCase(ch);
}
});
然⽽,所有这些真的感觉很重。重到实际上多数Java程序员都不会惹这个⿇烦。他们会宁愿写个循环并漠视他们代码⾥复杂性的累加。另⼀⽅⾯,Scala⾥的函数式⽂本真的很轻量,于是就频繁被使⽤。随着对Scala的逐步了解,你会发现越来越多定义和使⽤你⾃⼰的控制抽象的机会。你将发现这能帮助避免代码重复并因此保持你的程序简短和清晰。
14.3.4 Scala是静态类型的
静态类型系统认定变量和表达式与它们持有和计算的值的种类有关 。Scala坚持作为⼀种具有⾮常先进的静态类型系统的语⾔。从Java那样的内嵌类型系统起步,能够让你使⽤泛型:generics参数化类型,⽤交集:intersection联合类型和⽤抽象类型:abstract type隐藏类型的细节。这些为建造和组织你⾃⼰的类型打下了坚实的基础,从⽽能够设计出即安全⼜能灵活使⽤的接⼝。静态类型系统的经典优越性将更被赏识,其中最重要的包括程序抽象的可检验属性,安全的重构,以及更好的⽂档。
可检验属性 。静态类型系统可以保证消除某些运⾏时的错误。例如,可以保证这样的属性:布尔型不
会与整数型相加;私有变量不会从类的外部被访问;函数带了正确个数的参数;只有字串可以被加到字串集之中 。不过当前的静态类型系统还不能查到其他类型的错误。⽐⽅说,通常查不到⽆法终结的函数,数组越界,或除零错误。同样也查不到你的程序不符合式样书(假设有这么⼀份式样书)。静态类型系统因此被认为不很有⽤⽽被忽视。舆论认为既然这种类型系统只能发现简单错误,⽽单元测试能提供更⼴泛的覆盖,⼜为何⾃寻烦恼呢?我们认为这种论调不对头。尽管静态类型系统确实不能替代单元测试,但是却能减少⽤来照顾那些确需测试的属性的单元测试的数量。同样,单元测试也不能替代静态类型。总⽽⾔之,如Edsger Dijkstra所说,测试只能证明存在错误,⽽⾮不存在。因此,静态类型能给的保证或许很简单,但它们是⽆论多少测试都不能给的真正的保证 。
安全的重构 。静态类型系统提供了让你具有⾼度信⼼改动代码基础的安全⽹。试想⼀个对⽅法加⼊额外的参数的重构实例。在静态类型语⾔中,你可以完成修改,重编译你的系统并容易修改所有引起类型错误的代码⾏。⼀旦你完成了这些,你确信已经发现了所有需要修改的地⽅。对其他的简单重构,如改变⽅法名或把⽅法从⼀个类移到另⼀个,这种确信都有效。所有例⼦中静态类型检查会提供⾜够的确认,表明新系统和旧系统可以⼀样的⼯作 。
⽂档 。静态类型是被编译器检查过正确性的程序⽂档。不像普通的注释,类型标注永远都不会过期(⾄少如果包含它的源⽂件近期刚刚通过编译就不会)。更进⼀步说,编译器和集成开发环境可以利⽤类型标注提供更好的上下⽂帮助。举例来说,集成开发环境可以通过判定选中表达式的静态类型,
到类型的所有成员,并全部显⽰出来。
虽然静态类型对程序⽂档来说通常很有⽤,当它们弄乱程序时,也会显得很讨厌。标准意义上来说,有⽤的⽂档是那些程序的读者不可能很容易地从程序中⾃⼰想出来的。在如下的⽅法定义中:
def f(x: String) = ...
知道f的变量应该是String是有⽤的。另⼀⽅⾯,以下例⼦中两个标注⾄少有⼀个是讨厌的:
val x: HashMap[Int, String] = new HashMap[Int, String]()
很明显,x是以Int为键,String为值的HashMap这句话说⼀遍就够了;没必要同样的句⼦重复两遍。
Scala有⾮常精于此道的类型推断系统,能让你省略⼏乎所有的通常被认为是讨厌的类型信息。在上例中,以下两个不太讨厌的替代品也能⼀样⼯作:
val x = new HashMap[Int, String]()
val x: Map[Int, String] = new HashMap()
Scala⾥的类型推断可以⾛的很远 。实际上,就算⽤户代码丝毫没有显式类型也不稀奇。因此,Scala
编程经常看上去有点像是动态类型脚本语⾔写出来的程序。尤其显著表现在作为粘接已写完的库控件的客户应⽤代码上。⽽对库控件来说不是这么回事,因为它们常常⽤到相当精妙的类型去使其适于灵活使⽤的模式。这很⾃然。综上,构成可重⽤控件接⼝的成员的类型符号应该是显式给出的,因为它们构成了控件和它的使⽤者间契约的重要部分。
14.4 实验步骤
在spark-shell中编写WordCount代码和运⾏。
上传in.txt⽂件到HDFS上。
请⼤家参照实验⼀⾃⾏完成。
启动spark-shell。
[root@master spark]# cd /usr/cstor/spark
[root@master spark]# bin/spark-shell --master spark://master:7077
写⼊wordcount的scala代码并运⾏。
scala> val File("hdfs://master:8020/user/spark/")
scala> val count=file.flatMap(line => line.split(" ")).map(word => (word,1)).reduceByKey(_+_)
scala> llect()
14.5 实验结果
14.3.2输⼊(in.txt) (数据统⼀放在/root/data/14⽬录下)
hello world
ni hao
hello my friend
ni are my sunshine
14.3.2结束后运⾏结果如图14-1所⽰。
图14-1
实验操作:
步骤1:搭建Spark集
配置Spark集(独⽴模式):
1.前提:
配置各节点之间的免密登录,并在/etc/hosts中写好hostname与IP的对应,这样⽅便配置⽂件的相互拷贝。2、因为下⾯实验涉及Spark 集使⽤HDFS,所以按照之前的实验预先部署好HDFS。
在master机上操作:确定存在spark。
[root@master ~]# ls /usr/cstor
spark/
[root@master ~]#
在master机上操作:进⼊/usr/cstor⽬录中。
[root@master ~]# cd /usr/cstor
[root@master cstor]#
进⼊配置⽂件⽬录/usr/cstor/spark/conf, 先拷贝并修改plae为slave。
[root@master ~]# cd /usr/cstor/spark/conf
[root@master cstor]# cp plate slaves
然后⽤vim命令编辑器编辑slaves⽂件
[root@master cstor]# vim slaves
编辑slaves⽂件将下述内容添加到slaves⽂件中。
slave1
slave2
slave3
上述内容表⽰当前的Spark集共有三台slave机,这三台机器的机器名称分别是slave1~3。
在spark-conf.sh中加⼊JAVA_HOME。
[root@master cstor]# vim /usr/cstor/spark/sbin/spark-config.sh
加⼊以下内容
export JAVA_HOME=/usr/local/jdk1.7.0_79
将配置好的Spark拷贝⾄slaveX、client。(machines在⽬录/root/data/2下,如果不存在则⾃⼰新建⼀个)使⽤for循环语句完成多机拷贝。
[root@master ~]# cd /root/data/2
[root@master ~]# cat machines
slave1
slave2
slave3
client
[root@master ~]# for x in `cat machines` ; do echo $x ; scp -r /usr/cstor/spark/ $x:/usr/cstor/; done;
在master机上操作:启动Spark集。
[root@master local]# /usr/cstor/spark/sbin/start-all.sh
2.配置HDFS
配置Spark集使⽤HDFS:
⾸先关闭集(在master上执⾏)
[root@master ~]# /usr/cstor/spark/sbin/stop-all.sh
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论