这两天看了下 Spark 源码,感叹 Scala 的强大,不愧是 killer 级别的产品。想想 golang 果然是两个风格的语言。没有线上Spark部署及使用经验,仅仅按照官网执行过 SparkPi 示例,也就是 Hello World 水平。
我对 Spark 的认知:
1. 重度依赖 HDFS,可以使用单机文件,但效率肯定不行,特别是海量数据
2. 因为数据选择持久化在内存中,所以计算速度快。尤其适用于机器学习,对数据反复蹂躏的场景
3. 上层可以构建 Spark SQL 方便查询,类似 Hive,并且老版本就是基于 Hive
4. 部署也比较简单,有三种模式:standalone, mesos, yarn
5. 典型的master-worker 工作方式,特别类似以前做过的 mesos
6. RDD模型看懂了,无非就是分布式的数据集,按着一定规则做分片。核心在于Iterator,可以使transformation阶段不必计算,等action触发后再执行,一套流式下来非常完美。
7. DAGScheduler 是整个spark 的核心,如果说RDD是静态的数据集操作描述,那么DAGScheduler就是动态的对RDD做执行规划
Rpc 框架
先庖丁解牛,从底层业务无关Rpc看起。官方 Jira 一直要摆税对 Akka 的依赖,一个是想让 Rpc 插件化,一个原因是 Scala 的东西升级变化太快,版本兼容做的不够好。当前1.6做的还不够彻底。
当前支持 netty 和 akka 两种框架,可以看到在 rpcEnvNames 这个 Map 里注册了两个工厂方法,具体使用哪一个由配置决定,默认是 netty。 生成的 rpcEnv 是什么呢? 不是具体的 rpc 对象,而是类似 rpc 对象的管理者。就像用 Go 写 Redis Proxy时,有一个 Sessions Mana 来管理会话一样。rpcEnv就像一个容器,保存着自身 rpc 对象和连接的远端 rpc,屏蔽底层不同协议之间的差异。
在 Spark 中具体的 rpc 对象叫 endpoint(实在是没法翻译),worker节点是一个 endpoint, master 节点也是一个 endpoint, 包括其它的 driver, application, executorbackend。
单机时任务执行的 backend 是CoarseGrained, 实现了 ThreadSafeRpcEndPoint, 虽说是线程安全,但实现全部是加上 synchronized 做到的,worker, master 无一例外。
为了屏蔽上层调用者的差异,框架层面只做基本的业务无关操作,业务相关的初始化全部由回调完成。我们都知道比如 Akka,业务逻辑全部在 PreStart 这类接口内完成,一般业务需要重写。
这块代码很好理解,调用 actorSystem 系统生成自已的 rpc endpointRef,因为要连别的 rpc 自已也要有。
val actorRef 值由后面的函数执行后生成,是一个 actorRef。这是一个惰性的,直到下面 AkkaRpcEndpointRef 生成了 endpointRef, 再调用 init() 函数后才执行。
跟踪到这里,开始执行 actorRef,回头再看 actorRef ,是一个标准的 Akka actor 生成函数。在正式对外提供服务前,会调用几个重写的方法,这里面是 preStart(), 这个preStart 会调用 endpoint 的 onStart 方法,这个endpoint 就是具体的业务对象。 worker 节点要生成N个 threadpool, 连接master, balabala 一大堆,master 节点要做选举,生成 N个 threadpool,等待 worker 连接,等待 Application driver 发送的任务,一大堆 balabala。
而这些正式业务逻辑,全部由 endpoint 重写 receive 这个 akka 的偏函数实现。至此业务无关的 rpc 分析完。
有理解错误的,请大家指证~_~ 未完待续 ,todo......