分佈式存儲出現是因為單機存儲資源有限且難以擴展,分佈式計算出現同樣是因為單機計算資源有限且難以擴展。存儲資源一般指磁盤容量,而計算資源通常指內存和 CPU。
我們引用中科院的定義來詳細看一下分佈式計算到底是什麼:
分佈式計算是一種新的計算方式。所謂分佈式計算就是在兩個或多個軟件互相共享信息,這些軟件既可以在同一臺計算機上運行,也可以在通過網絡連接起來的多臺計算機上運行。
分佈式計算比起其它算法具有以下幾個優點:
1、稀有資源可以共享。
2、通過分佈式計算可以在多臺計算機上平衡計算負載。
3、可以把程序放在最適合運行它的計算機上。
這個定義真的是精闢,不僅指出了分佈式計算的內涵,也同樣指出了分佈式計算的問題。我們分解一下這個定義。
首先我們標註一些關鍵詞:軟件,網絡,運行,資源,共享,平衡,負載,適合。
我們不看上面的定義,只看這些關鍵詞,便可以勾勒出分佈式計算需要的子系統:
1、調度系統:對應關鍵詞 平衡,適合,負載 --> 想要生活的井井有條,最好有人幫你安排好。
2、通信系統:對應關鍵詞 網絡,共享 --> 想要溝通一起玩耍,至少有個電話吧。
3、計算系統:對應關鍵詞 軟件,資源,運行 --> 想要吃飯,至少有個活廚子吧。
同樣,我們可以先列舉這些關鍵詞的對立面:網絡不可靠,運行出問題,資源不足,共享不暢,分配不平衡,負載過大,不適合 根據這些反向關鍵詞勾勒出分佈式系統可能存在的問題:
1、資源調度不合理,導致負載不平衡,不能充分使用集群資源: 對應關鍵字 資源不足,分配不平衡,負載過大,不適合。
2、計算可靠性不高導致結果錯誤或者計算失敗 : 對應關鍵字 網絡不可靠,運行出問題。
調度問題佔了 4 個關鍵字,佔總關鍵字的 67 %,說明中科院認為調度問題是分佈式計算的最關鍵因素,是的,我們也是這麼認為的。所以我們會首先介紹 spark 的調度系統,然後帶出其他兩個系統,順便將 spark 解決兩個問題的方案帶出來。
Spark 框架
Spark 的調度系統從上到下包括 DAGScheduler(Job 級調度器)和TaskScheduler(Task 級調度器)和 SchedulerBackend(調度資源管理器)組成,其關係如下圖所示:
為了讓大家快速對這個流程有一個直觀的認識,我們參照例子解釋一下:
學校剛來的校長(DAGScheduler)決定梳理一下學校現狀,統計學校男女學生的人數,出一份報表(Job - 輸出男女人數報表),決定讓秘書先找人按照班級各自統計一下男女人數,每個班產生一個報表,然後讓合併男生和女生的數量(兩個 Stage - 班級報表,報表合併),校長同時規定了最多派多少組人(Executor - 實際執行工作的環境,對應 Java 虛擬機)去幹這件事,每個組最多多少人(Thread - 單個 Executor 中最大的並行工作個體,對應 CPU 核數 - 每個 Thread 只能幹單一的活,要不只統計男生數量,要不只統計女生數量)。
秘書拿到指令後,根據班級的數量,班級人數和班級的位置對組進行具體工作的規劃(比如說哪組人負責那幾個班級最快-離得最近)。
然後秘書到外包公司集團(SchedulerBackend - 專門外包人力的組織)按照規劃要人,外包集團會將人力需求分配到各個公司(Worker),如果正好所有人都被派出了,就告訴秘書等一會兒(Spark 程序啟動時的 Accept 狀態),一旦有人回來了,外包集團就讓這個人按照規劃去對應的幾個班級統計男女人數(Task - 數量等於班級數 * 2 - 因為每個班都要統計男生和女生數量),隨著人員逐漸迴歸,派出的人數也會逐漸跟上規劃。
校長會跟蹤統計的情況,每當有一個班級的男生或者女生被統計出來了,校長就掏出小本在待辦清單上把這個 Task 抹掉,直到所有的都抹掉了,然後去看每個班報表是否都生成了,就認為班級統計(第一個 Stage )完成了,同理,校長也會統計 Stage 數量用來判斷 Job 是否完成了。
雖然這個工作使用 Spark 的框架做有點殺雞用牛刀,但是上面的例子應該會讓大家對於其中的概念有一個大概的認識,我們下面再對這些概念給出相對嚴格的定義。
- Job 指一次 action 操作,Spark 對數據的操作包括 transform 操作和 action 操作,action 操作指有數據輸出(無論是輸出到文件還是輸出到控制檯)的操作,spark 是懶的,一段程序只有遇到 action 操作才會真正調度執行。Spark 按照 action 操作對應用進行 Job 切分。
- Stage 一個 Job 會根據 Shuffle 被拆分為多個 Stage 。所謂的 shuffle 就是寬依賴。所謂的寬依賴就是父 RDD 的分區影響了多個 RDD 的分區。
- Task Spark 最小執行單元,一般而言數據每個 partition 對應一個 Task。
- Driver 就是代碼中的 SparkContext,整個 Spark 作業啟動、調度、監控者。
- Worker 可運行的物理節點。
- Executor 執行 Spark 的處理程序,也就是一個 JVM進程,如果使用 yarn 調度的話則對應 yarn 中的一個 Container。
這些概念的關係在上面的例子中便可以推演出來,官方的圖則更加簡明一些:
雖然 Spark 有自己的調度機制和調度邏輯,但是也只能實現多個提交的 Job 內做出優化,如果要針對集群資源情況和負載壓力做出最佳的分配決策,僅憑 Spark 本身是做不到的。所以在工程應用上,很少會單獨部署和使用 Spark ,而是將其與 yarn 結合起來,利用 yarn 資源管理和分配能力給出最優的分配策略。
實際上, MapReduce 和 Storm 也通常會依賴 yarn 的資源管理能力。這也就是上文中第一個問題的答案——不要逞能,專業的事情交給專業的人做。
Spark 的內部通信是非常複雜的,client 與 master 之間, master 與 worker 之間 ,worker 內部之間都有通信,而且整個運作時很快的,資源消耗也是比較大的。所以不會採用阻塞 I/O 的方式在線等待,理所當然的選擇了 NIO(發出響應後立刻返回,釋放資源,等待回調)模型。實際上,幾乎沒有哪個大數據在線系統會不採用這種方案。在 1.6 之前,Spark 採用了基於 Actor 模型的 Akka 通信框架,但是因為一些管理和依賴衝突的考慮,在 1.6 以後替換成了 Netty 框架。
Spark 也使用了一系列的超時機制來防止網絡異常帶來的問題, 通過累加器計數來識別運行時的異常並會使用 Stage 重新提交來解決這些異常。
閱讀更多 訊宜捷科技 的文章