用Akka實現spark底層的通信框架

基本思路

Master:

package com.ycit.akka.rpc
import akka.actor.{Actor,ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
import scala.collection.mutable

/**
  * @author jpf
  * @date 2019/6/5 15:34
  */
class Master(val host:String,val port:Int) extends Actor  {
  private val idToWorker = new mutable.HashMap[String,WorkerInfo]()
  private val workers = new mutable.HashSet[WorkerInfo]
  //超時檢查的間隔
  val CHECK_INTERVAL = 15000
  override def preStart(): Unit = {
    //導入隱式轉換
    import context.dispatcher //使用timer太low了, 可以使用akka的, 使用定時器, 要導入這個包
    context.system.scheduler.schedule(0 millis, CHECK_INTERVAL millis, self, CheckTimeOutWorker)
  }
  //用於接受消息
  override def receive: Receive = {
    case RegisterWorker(id,memory,cores) =>{
    if(!idToWorker.contains(id)){
      val workerInfo = new WorkerInfo(id,memory,cores)
      idToWorker(id)=workerInfo
      workers +=workerInfo
      sender ! RegisteredWorker(s"akka.tcp://MasterSystem@$host:$port/user/Master")// 通知worker註冊
    }
    }
    case Heartbeat(workerId)=>{
      if(idToWorker.contains(workerId)){
        val workerInfo = idToWorker(workerId)
        //報活
        val currentTime = System.currentTimeMillis()
        workerInfo.lastHeartbeatTime=currentTime
      }
    }
    case CheckTimeOutWorker=>{
      val  currentTime = System.currentTimeMillis()
      val tomove = workers.filter(x=>currentTime-x.lastHeartbeatTime>CHECK_INTERVAL)
      for(wk<-tomove){
        workers -=wk
        idToWorker -=wk.id
      }
      println("當前存在的worker:"+"  "+workers.size)
    }
  }
}
object Master{
  def main(args: Array[String]): Unit = {
    val host = args(0)
    val port = args(1).toInt
    // 準備配置
    val configStr =
      s"""
         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
         |akka.remote.netty.tcp.hostname = "$host"
         |akka.remote.netty.tcp.port = "$port"
       """.stripMargin
    val config = ConfigFactory.parseString(configStr)
    val masterSystem = ActorSystem("MasterSystem",config)
    //創建Actor 起個名字
    masterSystem.actorOf(Props(new Master(host,port)),"Master")
    masterSystem.awaitTermination()//讓進程等待先別結束
  }
}

Worker:

package com.ycit.akka.rpc

import java.util.UUID

import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
import scala.language.postfixOps

/**
  * @author jpf
  * @date 2019/6/5 15:34
  */
class Worker(val masterHost:String,val  masterPort:Int,val memory:Int,cores:Int) extends Actor  {
  val workId = UUID.randomUUID().toString
  var master : ActorSelection = _
  val HEART_INTERVAL = 10000
  override def preStart(): Unit = {
    //在master啓動時會打印下面的那個協議, 可以先用這個做一個標誌, 連接哪個master
    //繼承actor後會有一個context, 可以通過它來連接
    master = context.actorSelection(s"akka.tcp://MasterSystem@$masterHost:$masterPort/user/Master")
    master ! RegisterWorker(workId,memory,cores)
  }
  override def receive: Receive = {
    //接受master的迴應消息
    case RegisteredWorker(masterPath)=>{
      println(masterPath)
      //啓動定時器發送心跳
     import context.dispatcher  //引入隱式轉換對象
      context.system.scheduler.schedule(0 millis,HEART_INTERVAL millis,self,SendHeartbeat)
    }
    case SendHeartbeat =>{
      println("send heartbeat to master")
      master ! Heartbeat(workId)
    }
  }
}
object Worker{
  def main(args: Array[String]): Unit = {
    val host = "127.0.0.1"
    val port = 9999
    val masterHost = "127.0.0.1"
    val masterPort = 8888
    val memory = 1024
    val cores = 2
    // 準備配置
    val configStr =
      s"""
         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
         |akka.remote.netty.tcp.hostname = "$host"
         |akka.remote.netty.tcp.port = "$port"
       """.stripMargin
    val config = ConfigFactory.parseString(configStr)
    //ActorSystem老大,輔助創建和監控下面的Actor,他是單例的
    val WorkerSystem = ActorSystem("WorkerSystem",config)
    //創建Actor 起個名字
    WorkerSystem.actorOf(Props(new Worker(masterHost,masterPort,memory,cores)),"Worker")
    WorkerSystem.awaitTermination()//讓進程等待先別結束
  }
}

RemoteMessage (發送消息的樣例類)

package com.ycit.akka.rpc

/**
  * @author jpf
  * @date 2019/6/5 15:34
  */
trait RemoteMessage extends Serializable

//Worker -> Master
case class RegisterWorker(id: String, memory: Int, cores: Int) extends RemoteMessage

case class Heartbeat(id: String) extends RemoteMessage


//Master -> Worker
case class RegisteredWorker(masterUrl: String) extends RemoteMessage

//Worker -> self
case object SendHeartbeat

// Master -> self
case object CheckTimeOutWorker

由於寫Java寫習慣了爲RemoteMessage接口後面加個{ }導致RemoteMessage 裏面的樣例類不能被序列化(困擾了我好久)

WorkerInfo(實體類)

package com.ycit.akka.rpc

/**
  * @author jpf
  * @date 2019/6/5 15:34
  */
class WorkerInfo(val id: String, val memory: Int, val cores: Int) {


  var lastHeartbeatTime : Long = _
}

 

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