本文基于 incubator-livy 0.4.0-incubating
Livy Session 詳解(上) - 簡書 一文主要介紹了 session 整體的啟動流程并詳細分析了 client 端(livy server 端)是如何啟動 driver 以及建立連接的。本文將進一步分析 session server 端(即 driver 內(nèi)部)是如何啟動、初始化的以及執(zhí)行代碼片段的。
注:如果對 livy 的整體架構(gòu)以及 session client 端不了解,請先閱讀以下兩篇相關(guān)文章:
一、整體啟動、初始化流程
如上圖所示,driver 內(nèi)部的啟動流程可以分為以下五個步驟:
- 創(chuàng)建 ReplDriver 實例
- 初始化 server
- 初始化 SparkContext
- 創(chuàng)建 JobContextImpl 實例并執(zhí)行 jobs
- 等待退出
1.1、創(chuàng)建 ReplDriver 實例
ReplDriver 是 InteractiveSession 對應(yīng)的 Spark App driver,用來接收 livy server 的各種請求并進行處理。也是 RSCDriver 的子類,RSCDriver:
- 持有等待 RSCClient 進行連接的
RpcServer server
- 初始化 SparkContext
- 處理各種請求:CancelJob、EndSession、JobRequest、BypassJobRequest、SyncJobRequest、GetBypassJobStatus
- 處理 add file 請求
除了能處理 RSCDriver 支持的請求外,ReplDriver 還能處理:BaseProtocol.ReplJobRequest、BaseProtocol.CancelReplJobRequest、BaseProtocol.GetReplJobResults 請求,這些請求對應(yīng)的是序列化的 job (GitHub - cloudera/livy: Livy is an open source REST interface for interacting with Apache Spark from anywhere)相關(guān)的請求。
1.2、初始化 server
這一步在 RSCDriver#initializeServer()
中調(diào)用,用于連接 client 并告知 server 端 rpc 地址,client 獲知 server rpc 地址后會進行連接并發(fā)送請求。
1.3、初始化 SparkContext
1.3.1、創(chuàng)建解釋器
會根據(jù)不同的 kind 創(chuàng)建不同類型的解釋器,kind 在創(chuàng)建 session 的 request body 中指定。這些解釋器有繼承共同的 treat Interpreter,其類圖如下:
其中的 execute 方法用來執(zhí)行代碼片段:
- pyspark 類型的解釋器用于執(zhí)行 python、pyspark 代碼片段
- pyspark3類型的解釋器用于執(zhí)行 python3、 python3 spark 代碼片段
- spark 類型的解釋器用于執(zhí)行 scala、scala spark 代碼片段
- sparks 類型的解釋器用于執(zhí)行 r、r spark 代碼片段
1.3.2、創(chuàng)建 repl/Session
repl/Session(用于和 sessions/Session
進行區(qū)分,后文簡稱 Session)是 server 端中至關(guān)重要的類。主要職責(zé)是:
- 啟動 interpreter,并獲取 SparkContext
- 持有線程池來異步執(zhí)行 statements(通過 interpreter 來執(zhí)行)
- 持有線程池來異步取消 statements
- 管理一個 session 下所有的 statements
在構(gòu)造 Session 的過程中,會初始化用于執(zhí)行 statement 的 interpreterExecutor,如下:
private val interpreterExecutor = ExecutionContext.fromExecutorService(
Executors.newSingleThreadExecutor())
可以看到,這個線程只有一個線程,也就是說在一個 Session 中的 statement 是串行的,一個 statement 執(zhí)行完才會執(zhí)行下一個。這種串行的方式有明顯的弊端,即當(dāng) Session 的資源足以執(zhí)行多個 statement 時,也只能一個接著一個執(zhí)行,這既浪費了資源,有延長了任務(wù)運行的整體時間。那為什么還要這么做呢?主要是因為目前 livy 中的一個 Session 僅包含一個 interpreter,如果一個 interpreter 同時執(zhí)行多段代碼片段,很容易會出現(xiàn)穿插執(zhí)行的錯誤。要解決這一困境的思路主要有兩個:
- 不使用 interpreter 來執(zhí)行代碼片段
- 一個 Session 包含多個 interpreter,每個 interpreter 同一時間也只執(zhí)行一個 statement
目前,我們正在做這方面的工作,等完工之后可以再進一步說明下。
1.3.3、啟動 Session
主要是調(diào)用 interpreter#start
,該啟動也是提交到 interpreterExecutor 中執(zhí)行的,在啟動后就會將 Session 的 state 修改為 idle。我們來看看 Spark 類型的 Session 的 interpreter 啟動過程:
SparkInterpreter#start()
以上,就是 Session server 端的詳細的啟動過程,下一篇我們將看看代碼片段是怎么執(zhí)行的。