Livy Session 詳解(中)

本文基于 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)部的啟動流程可以分為以下五個步驟:

  1. 創(chuàng)建 ReplDriver 實例
  2. 初始化 server
  3. 初始化 SparkContext
  4. 創(chuàng)建 JobContextImpl 實例并執(zhí)行 jobs
  5. 等待退出

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é)是:

  1. 啟動 interpreter,并獲取 SparkContext
  2. 持有線程池來異步執(zhí)行 statements(通過 interpreter 來執(zhí)行)
  3. 持有線程池來異步取消 statements
  4. 管理一個 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í)行的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容