歡迎轉載,轉載請注明出處,徽滬一郎。
楔子
源碼閱讀是一件非常容易的事,也是一件非常難的事。容易的是代碼就在那里,一打開就可以看到。難的是要通過代碼明白作者當初為什么要這樣設計,設計之初要解決的主要問題是什么。
在對Spark的源碼進行具體的走讀之前,如果想要快速對Spark的有一個整體性的認識,閱讀Matei Zaharia做的Spark論文是一個非常不錯的選擇。
在閱讀該論文的基礎之上,再結合Spark作者在2012 Developer Meetup上做的演講Introduction to Spark Internals,那么對於Spark的內部實現會有一個比較大概的了解。
有了上述的兩篇文章奠定基礎之后,再來進行源碼閱讀,那么就會知道分析的重點及難點。
基本概念(Basic Concepts)
RDD - resillient distributed dataset 彈性分布式數據集
Operation - 作用於RDD的各種操作分為transformation和action
Job - 作業,一個JOB包含多個RDD及作用於相應RDD上的各種operation
Stage - 一個作業分為多個階段
Partition - 數據分區, 一個RDD中的數據可以分成多個不同的區
DAG - Directed Acycle graph, 有向無環圖,反應RDD之間的依賴關系
Narrow dependency - 窄依賴,子RDD依賴於父RDD中固定的data partition
Wide Dependency - 寬依賴,子RDD對父RDD中的所有data partition都有依賴
Caching Managenment -- 緩存管理,對RDD的中間計算結果進行緩存管理以加快整體的處理速度
編程模型(Programming Model)
RDD是只讀的數據分區集合,注意是數據集。
作用於RDD上的Operation分為transformantion和action。 經Transformation處理之后,數據集中的內容會發生更改,由數據集A轉換成為數據集B;而經Action處理之后,數據集中的內容會被歸約為一個具體的數值。
只有當RDD上有action時,該RDD及其父RDD上的所有operation才會被提交到cluster中真正的被執行。
從代碼到動態運行,涉及到的組件如下圖所示。
演示代碼
val sc = new SparkContext("Spark://...", "MyJob", home, jars)
val file = sc.textFile("hdfs://...")
val errors = file.filter(_.contains("ERROR"))
errors.cache()
errors.count()
運行態(Runtime view)
不管什么樣的靜態模型,其在動態運行的時候無外乎由進程,線程組成。
用Spark的術語來說,static view稱為dataset view,而dynamic view稱為parition view. 關系如圖所示
在Spark中的task可以對應於線程,worker是一個個的進程,worker由driver來進行管理。
那么問題來了,這一個個的task是如何從RDD演變過來的呢?下節將詳細回答這個問題。
部署(Deployment view)
當有Action作用於某RDD時,該action會作為一個job被提交。
在提交的過程中,DAGScheduler模塊介入運算,計算RDD之間的依賴關系。RDD之間的依賴關系就形成了DAG。
每一個JOB被分為多個stage,划分stage的一個主要依據是當前計算因子的輸入是否是確定的,如果是則將其分在同一個stage,避免多個stage之間的消息傳遞開銷。
當stage被提交之后,由taskscheduler來根據stage來計算所需要的task,並將task提交到對應的worker.
Spark支持以下幾種部署模式1)standalone 2)Mesos 3) yarn. 這些部署模式將作為taskscheduler的初始化入參。
RDD接口(RDD Interface)
RDD由以下幾個主要部分組成
- partitions -- partition集合,一個RDD中有多少data partition
- dependencies -- RDD依賴關系
- compute(parition) -- 對於給定的數據集,需要作哪些計算
- preferredLocations -- 對於data partition的位置偏好
- partitioner -- 對於計算出來的數據結果如何分發
緩存機制(caching)
RDD的中間計算結果可以被緩存起來,緩存先選Memory,如果Memory不夠的話,將會被寫入到磁盤中。
根據LRU(last-recent update)來決定哪先內容繼續保存在內存,哪些保存到磁盤。
容錯性(Fault-tolerant)
從最初始的RDD到衍生出來的最后一個RDD,中間要經過一系列的處理。那么如何處理中間環節出現錯誤的場景呢?
Spark提供的解決方案是只對失效的data partition進行事件重演,而無須對整個數據全集進行事件重演,這樣可以大大加快場景恢復的開銷。
RDD又是如何知道自己的data partition的number該是多少?如果是hdfs文件,那么hdfs文件的block將會成為一個重要的計算依據。
集群管理(cluster management)
task運行在cluster之上,除了spark自身提供的standalone部署模式之外,spark還內在支持yarn和mesos.
Yarn來負責計算資源的調度和監控,根據監控結果來重啟失效的task或者是重新distributed task一旦有新的node加入cluster的話。
這一部分的內容需要參考yarn的文檔。
小結
在源碼閱讀時,需要重點把握以下兩大主線。
- 靜態view 即 RDD, transformation and action
- 動態view 即 life of a job, 每一個job又分為多個stage,每一個stage中可以包含多個rdd及其transformation,這些stage又是如何映射成為task被distributed到cluster中
參考資料(reference)
- Introduction to Spark Internals http://files.meetup.com/3138542/dev-meetup-dec-2012.pptx
- Resilient Distributed Datasets: A Fault-tolerant Abstraction for In-Memory Cluster Computing https://www.usenix.org/system/files/.../nsdi12-final138.pdf
- Lightning-Fast Cluster Computing with Spark and Shark http://www.meetup.com/TriHUG/events/112474102/
