第一阶段:Spark streaming、spark sql、kafka、spark内核原 理(必须有一个大型项目经验);
第二阶段:spark运行的各种环境,各种故障的解决,性能 优化(精通spark内核、运行原理);
第三阶段:流处理、机器学习为鳌头,需要首先掌握前两个 阶段的内容;
跟随王家林老师的零基础讲解,注重动手实战,成为spark高手,笑傲大数据之林!
第一部分:学习笔记
本期内容:
1 Scala隐式彻底详解
2 Scala并发编程详解
3 Spark源码阅读及作业
一、隐式转换
隐式转换只是普通的方法,唯一特殊的地方是它以修饰符implicit开始,implicit告诉scala编译器可以在一些情况下自动调用(比如说如果当前类型对象不支持当前操作,那么scala编译器就会自动添加调用相应隐式转换函数的代码,将其转换为支持当前操作的类型的对象,前提是已经存在相应的隐式转换函数且满足作用域规则),而无需你去调用(当然如果你愿意,你也可以自行调用)。
mplicit def rddToSequenceFileRDDFunctions[K <% Writable: ClassTag, V <% Writable: ClassTag]( rdd: RDD[(K, V)]) =
new SequenceFileRDDFunctions(rdd)
implicit def rddToOrderedRDDFunctions[K : Ordering : ClassTag, V: ClassTag]( rdd: RDD[(K, V)]) =
new OrderedRDDFunctions[K, V, (K, V)](rdd)
class Person(val name:String)
class Engineer(val name:String,val salary:Double){
def code = println("Coding ...")
}
//再定义一个方法
def toCode(p:Person){
p.code
}
implicit def person2Engineer(p:Person):Engineer ={
new Engineer(p.name,100000) //implicit定义的时候,必须写返回类型
}
//再次运行toCode方法
def toCode(p:Person){p.code}
roCode(new Person("Scala"))
class Level(Level:Int)
def toWorker(name:String)(impliciit level:Level)
{
println(name + ":" +level)
}
impliciit val level = new level(8)
toWorker("Spark") //调用toWorker
class level(level:Int)
def toWorker(name:String)(impliciit l:Level) =println(name + l.level)
impliciit val level = new level(8)
toWorker("Spark")
隐式转换函数定义如下:
implicit def functionName(…) = {…}
隐式转换满足以下规则:
1、作用域规则:scala编译器仅会考虑处于作用域之内的隐式转换。隐式转换要么是以 单一标识符的形式(即不能是aaa.bbb的形式,应该是bbb的形式) 出现在作用域中,要么是存在于源类型或者目标类型的伴生对象中。
2、单一调用规则:编译器在同一个地方只会添加一次隐式操作,不会在添加了一个隐 式操作之后再在其基础上添加第二个隐式操作。
3、显式操作先行规则:若编写的代码类型检查无误,则不会尝试任何隐式操作。
class RichFile(val file:File){
def read = Source.fromFile(file.getPath()).mkString
}
//RichFile有一个File参数,类似RichInt
object Context{
implicit def file2RichFile(file:File)= new RichFile(file)
//File -> RichFile隐式转换的时候一定要有implicit关键字,原类型file目标类型RichFile。
}
object Hello_Implicit_Conversions {
def main(args: Array[String]) {
import Context.file2RichFile
println(new File("E:\\Hello.txt").read)
1 to 3
//1是Int类型,to方法
}
}
我们需要某个类中的某个特殊的方法,但该类中没有提供此方法,所以使用隐式转换为提供了这个方法的类,然后再调用该方法。
第一步:使用增强的类如上述代码的RichFile;
第二部:写明隐式转换,有implicit关键字,将已有的类型增强成增强的类,即将File->RichFile,调用read方法
第三部:在主方法中,使用import导入隐式转换的函数file2RichFile*
二、隐式参数
柯里化函数的完整的最后一节参数可以被隐式提供,即隐式参数。此时最后一节参数必须被标记为implicit(整节参数只需一个implicit,并不是每个参数都需要),同时用来提供隐式参数的相应实际变量也应该标记为implicit的。对于隐式参数,我们需要注意的是:
隐式参数也可以被显式提供;
提供隐式参数的实际变量必须以单一标识符的形式出现在作用域中;编译器选择隐式参数的方式是通过匹配参数类型与作用域内的值类型,因此隐式参数应该
是很稀少或者很特殊的类型(最好是使用自定义的角色确定的名称来命名隐式参数类型),以便不会被碰巧匹配;如果隐式参数是函数,编译器不仅会尝试用隐式值补足这个参数,还会把这个参数当作可用的隐式操作而使用于方法体中。
object Context_Implicits{
implicit val default:String = "Flink"
}
object Param{
def print(content:String)(implicit language:String){
println(language+":"+content)
}
}
object Implicit_Parameters {
def main(args: Array[String]) {
Param.print("Spark")("Scala")
import Context_Implicits._
Param.print("Hadoop")
}
}
3、并发编程
(1)创建actor:
actor是一个类似于线程的实体,它有一个用来接收消息的邮箱。实现actor的方法是继承scala.actors.Actor特质并完成其act方法。你可以通过actor的start方法来启动它。actor在运行时都是相互独立的。你也可以使用scala.actors.Actor对象的actor方法来创建actor,不过此时你就无需再调用start方法,因为它在创建之后马上启动。
(2)发送接收消息:
Actor通过相互发送消息的方式进行通信,你可以使用“!”方法来发送消息,使用receive方法来接收消息,receive方法中包含消息处理的模式匹配(偏函数)。发送消息并不会导致actor阻塞,发送的消息在接收actor的邮箱中等待处理,直到actor调用了receive方法,如果actor调用了receive但没有模式匹配成功的消息,那么该actor将会阻塞,直到收到了匹配的消息。
创建actor并发送接收消息的示例如下:
object ScalaTest extends Actor {
def act() {
while (true) {
receive {
case msg => println(msg)
}
}
}
def main(args: Array[String]) {
start()
this ! "hello."
}
}
(3)将原生线程当作actor:
Actor子系统会管理一个或多个原生线程供自己使用。只要你用的是你显式定义的actor,就不需要关心它们和线程的对应关系是怎样的。该子系统也支持反过来的情形:即每个原生线程也可以被当作actor来使用。此时,你应该使用Actor.self方法来将当前线程作为actor来查看,也就是说可以这样使用了:Actor.self ! "message"。
import scala.actors.Actor
case name:String =>println(name)
val actor=new HiActor
actor.start()
actor ! "Spark" //发送消息,死循环
import scala.actors.Actor
class HiActor extends Actor{
def act(){
while(true) {
case name: String => println(name)
}
}
}
case class Basic(name:String,age:Int)
case class Worker(name:String,age:Int)
class basicActor extends Actor{
def act(){
while(true){ //收到邮件
receive{ //模式匹配
case Basic(name,age) =>println("Basic Information:" +name +age)
case Worker(name,age) =>println("Worker Information:" +name + age)
}
}
}
}
val b = new basicActor
b.start //启动
b ! Basic("Scala",13)
b ! Worker("Spark",7)
Actor相互发送消息
!?: 必须等到Actor消息处理完
val result = b !? Worker("Spark",7)
!!
val future = a !! M //发消息,未来某个时间获得结果
val result = future
第二部分:作业
解读DAGScheduler Master Worker中的并发编程和隐式转换
/*
* RDD的伴生对象中的子类使用隐式转换进行子类的功能增强
* 语法:
* 1、隐式方法 将rdd转化为rddToPairRDDFunctions
* 2、带有一个RDD[(K, V)]参数rdd
* 3、隐式转换rddToPairRDDFunctions中curring
* 4、new 一个目标对象PairRDDFunctions
*/
// The following implicit functions were in SparkContext before 1.3 and users had to
// `import SparkContext._` to enable them. Now we move them here to make the compiler find
// them automatically. However, we still keep the old functions in SparkContext for backward
// compatibility and forward to the following functions directly.
implicit def rddToPairRDDFunctions[K, V](rdd: RDD[(K, V)])
(implicit kt: ClassTag[K], vt: ClassTag[V], ord: Ordering[K] = null): PairRDDFunctions[K, V] = {
new PairRDDFunctions(rdd)
}
implicit def rddToAsyncRDDActions[T: ClassTag](rdd: RDD[T]): AsyncRDDActions[T] = {
new AsyncRDDActions(rdd)
}
implicit def rddToSequenceFileRDDFunctions[K, V](rdd: RDD[(K, V)])
(implicit kt: ClassTag[K], vt: ClassTag[V],
keyWritableFactory: WritableFactory[K],
valueWritableFactory: WritableFactory[V])
: SequenceFileRDDFunctions[K, V] = {
implicit val keyConverter = keyWritableFactory.convert
implicit val valueConverter = valueWritableFactory.convert
new SequenceFileRDDFunctions(rdd,
keyWritableFactory.writableClass(kt), valueWritableFactory.writableClass(vt))
}