spark job server原理

配置相關

  • settings.sh
    • 功能:配置環境變量
    • APP_USER/APP_GROUP:作業提交用戶和組
    • JMX_PORT:java jmx端口,通常在aws或者其他容器裏打開
    • INSTALL_DIR:sjs所做目錄
    • LOG_DIR:日誌路徑
    • PIDFILE:啓動sjs,產生pid存放的文件名
    • JOBSERVER_MEMORY:啓動spark作業的driverMem
    • SPARK_VERSION:指定spark版本
    • SCALA_VERSION:scala版本
    • SPARK_HOME、SPARK_LOG_DIR、SPARK_CONF_DIR:spark相關配置
    • YARN_CONF_DIR、HADOOP_CONF_DIR:yarn配置
  • local.conf
    • spark.master:指定spark提交的類型,yarn-client、local[4]等
    • spark.jobserver
      • port:指定jobServer的啓動端口,使用此端口進行作業提交和監控等
      • context-per-jvm:是否每個context都啓動一個獨立的進程
      • jobdao:指定處理jobs、jars等邏輯的類
      • datadao:通過POST/data上傳到sjs的文件存放路徑
      • sqldao:當jobdao指定爲JobSqlDAO時使用
        • slick-driver
        • jdbc-driver
        • rootdir:H2 driver存放數據目錄
        • jdbc:連接
        • dbcp:連接池
      • result-chunk-size
        • 作業返回值使用分塊傳輸,每塊大小
    • spark.contexts:啓動sjs自動加載的context配置
      • 名字
    • context-settings:啓動context,即app,相關配置
      • num-cpu-cores:core個數
      • memory-per-node:executor的mem,eg 512m、1G
      • dependent-jar-uris:依賴的jar包,list形式,或者字符串,使用逗號隔開
        • [“file:///xxx.jar”,”file:///xxx2.jar”],或者”file:///xxx.jar,file:///xxx2.jar”
      • 其他的spark配置,去掉前綴spark即可
        • 如:spark.speculation可配置爲speculation
  • server_start.sh
    • 啓動spark job server
  • manager_start.sh
    • context-per-jvm設置爲true時,纔會使用此腳本,用於啓動context

使用

  • 啓動
    • 運行腳本server_start.sh即可
  • 初始化context
    • curl -d "" 'ip:port/contexts/roncen_test_context?context-factory=spark.jobserver.context.HiveContextFactory'
  • 上傳jar包
    • curl -H "Content-Type: application/java-archive" --data-binary @/home/vipshop/platform/sjs_2.0/jars/job-server-extras_2.11-0.7.0-SNAPSHOT.jar ip:8091/binaries/sql
  • 提交作業
    • curl -d "sql_file=\"hdfs://bipcluster/spark/sql/test_cassandra.sql\"" 'ip:8091/jobs?appName=sql&classPath=spark.jobserver.vip.VipHiveJob&context=roncen_test_context&sync=false'
  • 通過jobId獲取job運行狀態
    • curl -v 'ip:8091/jobs/xxx
  • 刪除context
    • curl -X DELETE "ip:8091/contexts/roncen_test_context"

問題記錄

  • server返回失敗問題
  • [delete context時,context上的job並未結束]
  • 時不時返回The server was not able to produce a timely response to your request

問題1:The server was not able to produce a timely response to your request

  • 探測方法
    • curl -v 'ip:port/jobs/b2ee01d2-a495-43a3-a0e5-f2ba82330211'
    • 探測對應的jobId狀態
    • 正常情況下,返回:”RUNNING”|”ERROR”|”FINISHED”
  • 獲取job狀態邏輯
    • spark.jobserver.WebApi中接收http的GET請求GET /jobs/<jobId>
    • 通過akka從jobInfoActor中獲取job狀態GetJobStatus(jobId)
      • jobDao中獲取對應jobId的信息
        • JobSqlDao.getJobInfo()中,從數據庫中查詢對應job的信息,返回
    • 返回格式application/json給客戶端
      • jobId不存在:返回No such job ID xxxx
      • 存在:
        • 構造返回格式:jobId: , startTime: , classPath: , context: , duration: , status:
        • 通過akka從JobInfoActor中獲取job結果GetJobResult
          • 通過AkkaClusterSupervisorActorGetResultActor(context)得到對應的resultActor
            • 通過contextName得到對應的resultActor
            • -
          • 通過resultActorGetJobResult(jobId)得到最後的結果
        • 返回客戶端結果

初始化context步驟

  • 命令示例curl -d "" 'ip:port/contexts/sql-context-for-update-on-sale-85?context-factory=spark.jobserver.context.HiveContextFactory'
  • 通過http調用WebApi中的POST /contexts/<contextName>
  • 通過akka調用AkkaClusterSupervisorActorAddContext(cName, config)

    • 判斷是否存在,如果存在則返回ContextAlreadyExists
    • 調用方法startContext()

      • 生成contextActorName,”jobManager-” + uuid
      • 在${LOG_DIR}路徑下創建contextDir路徑,生成對應文件context.conf
        • 存放actornamecontext-factory等基礎信息
      • 生成執行命令:${deploy.manager-start-cmd} contextDir cluster.selfAddress(akka地址),即./manager_start.sh xxx,此命令是在後臺執行的,命令後有&
      • 判斷返回值,如果失敗,返回ContextInitError,如果成功,將其放入contextInitInfos的map中
      • 執行上述生成的命令
        • 執行主類spark.jobserver.JobManager
        • 獲取context.conf文件中的配置信息
        • 初始化jobDao,spark.jobserver.jobdao配置,這裏爲spark.jobserver.io.JobSqlDAO
        • 初始化JobDAOActor,命名爲dao-manager-jobmanager
        • 初始化jobManager,命名爲${context.actorname}
        • join到cluster中Cluster(system).join(clusterAddress) ????
          • 發送ActorIdentity(memberActors, actorRefOpt)AkkaClusterSupervisorActor
      • AkkaClusterSupervisorActor收到此消息後
        • 遍歷當前cluster所有的actorRef
          • 如果返回的actorName以jobManager開頭則執行以下步驟,否則不處理
          • contextInitInfos中remove當前actorName對應的actor
          • 執行方法initContext()
          • 初始化JobResultActor resultActor
          • 通過akka將resultActor發送給正在處理的actor,即發送消息JobManagerActor.Initialize(Some(resultActor))JobManagerActor
            • JobManagerActor得到消息後,進行如下處理
              • 初始化JobStatusActor
              • 得到JobResultActor,如果resultActor沒有,則初始化一個
              • 加載dependent-jar-uris指定的jar包
              • 生成contextFactory,生成context
              • 生成JobCacheImpl,用於緩存job信息
              • dependent-jar-uris指定的jar包放入sparkContext.addJar()中
              • 返回Initialized(contextName, resultActor),如果失敗,則返回InitError(t)
          • 得到返回值
            • 如果成功,則將當前context放到contexts中,即contexts(ctxName) = (ref, resActor)
    • 返回成功/失敗

  • 返回json類型結果

提交job到context中

  • 命令示例:curl -d "sql = \"show databases\"" 'ip:port/jobs?appName=sql&classPath=spark.jobserver.HiveTestJob&context=sql-context-for-gs-sku-check-85&sync=true'
  • 通過http調用WebApi中的POST /jobs
  • 通過akka中AkkaClusterSupervisorActorGetContext(name),得到對應context的jobManager
    • 如果沒有得到,則返回NoSuchContext或者ContextInitError(err)
    • 通過jobManager進行與context進程通信,發送JobManagerActor.StartJob,用於提交作業
      • 加載未加載的jar包
      • 調用startJobInternal()
        • 通過jobSqlDao,獲取當前appName上次提交作業的時間和type,如果沒有則返回錯誤
        • 隨機生成randomUUID,作爲jobId
        • 通過sparkContextFactory.loadAndValidateJob()生成jobContainer
          • 通過classPath/appname,在JobCacheImpl中獲取JobJarInfo,並初始化
            • 如果cache中沒有,會通過akka發送消息GetBinaryPath(),從jobSqlDao中獲取jar包
            • 初始化構造函數,將其放入JobContainer中,返回
        • 判斷返回值,如果爲Good(container),則繼續,否則返回錯誤
        • 將結果發送給JobResultActorJobStatusActor
          • JobStatusActor
            • 發送消息SaveJobInfo到jobSqlDao,將信息存入元數據庫
        • 調用方法getJobFuture()返回結果
          • 判斷當前runningJob是否大於最大運行job,如果是則返回NoJobSlotsAvailable(maxRunningJobs),否則繼續
          • 使用scala的Future,另起線程執行job
            • 設置SparkEnv
            • 發送消息JobInitJobStatusActor
            • 通過方法HiveTestJob.validate()判斷當前job是否正常
              • 如果正常
                • 發送消息JobStartedJobStatusActor
                • 設置sparkContext的jobGroup爲當前jobId,sc.setJobGroup(jobId, xxx)
                • 調用接口,執行job,HiveTestJob.runJob(jobC, jobEnv, jobData)
              • 否則發送JobValidationFailed
            • 線程執行結束
              • 成功
                • 發送JobFinishedJobStatusActor
                • 發送JobResultJobResultActor
              • 失敗
                • 發送JobErroredOutJobStatusActor
    • 判斷返回結果,並返回給客戶端對應的http reponse
      • JobResult(jobId, res)
      • JobErroredOut
      • JobStarted(_, jobInfo)
        • 通過akka發送給JobInfoActor消息StoreJobConfig(jobInfo.jobId, postedJobConfig)
          • JobInfoActor得到消息後,通過jobDao.saveJobConfig(jobId, jobConfig)存儲信息,這裏爲JobSqlDao
      • JobValidationFailed
      • NoSuchApplication
      • NoSuchClass
      • WrongJobType
      • WrongJobType
      • NoJobSlotsAvailable
      • ContextInitError

圖形化展示

架構展示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章