使用Akka实现一个简易版的spark通信框架

本文详细介绍了如何使用Akka Actor模型构建分布式计算系统。通过Master和Worker类的实现,展示了如何进行任务分配、心跳监测及资源管理。Master负责接收Worker的注册和心跳信息,维护Worker状态,并定期清理超时连接;Worker则定期向Master发送心跳,确保自身状态被Master所知。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

架构图

具体代码

Master类

import akka.actor.{Actor, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import com.zhe.rpc.{HeartBeat, RegisteredMessage, WorkerInfo}

import scala.concurrent.duration._
import scala.collection.mutable
import scala.collection.mutable.L
class Master extends Actor {

  //定义一个map集合用于存储worker的注册信息 key;workerId value:workerInfo
  private val id2WorkerInfoMap = new mutable.HashMap[String, WorkerInfo]()
  //定义一个lst集合用于存储workerinfo信息,主要方便与后期按照worker的资源大小排序
  private val workerInfoList = new ListBuffer[WorkerInfo]
  //定义master定时检查的时间间隔
  val checkOutTimeInterval = 15000 //15s

  override def preStart(): Unit = {
    //master定时检查超时worker
    //手动导入隐式转换
    import context.dispatcher
    context.system.scheduler.schedule(0 millis, checkOutTimeInterval millis,self,CheckOutTime)
  }

  override def receive: Receive = {
    //master接受worker的注册信息
    case registMessage(workerId, memory, cores) => {
      println("成功一个worker")
      //判断当前worker是否注册,master只接受没有注册的worker信息
      if (!id2WorkerInfoMap.contains(workerId)) {
        //构建workerinfo对象
        val workerInfo = new WorkerInfo(workerId, memory, cores)
        //保存到map集合中
        id2WorkerInfoMap.put(workerId, workerInfo)
        //保存到list集合中
        workerInfoList += workerInfo
        //master反馈注册成功信息给worker
        sender ! RegisteredMessage(s"workerId:$workerId 注册成功")
      }

    }
    //master接收worker的心跳信息
    case HeartBeat(workerId) => {
      //判断当前worker是否注册,master只接受已注册过的worker的心跳信息
      if (id2WorkerInfoMap.contains(workerId)) {
        //获取workerId对应的WorkerInfo
        val workerInfo: WorkerInfo = id2WorkerInfoMap(workerId);
        //获取当前系统时间
        val lastTime: Long = System.currentTimeMillis()
        //吧当前系统时间赋值给worker上一次心跳时间
        workerInfo.lastHeartBeatTime = lastTime
      }
    }
    //master接受master信息
    case CheckOutTime => {
      //判断worker超时逻辑:当前时间 - worker上一次心跳时间 > master定时检查的时间间隔
      //获取当前时间
      val now: Long = System.currentTimeMillis()
      val outTimeWorkerInfoes: ListBuffer[WorkerInfo] = workerInfoList.filter(x => now - x.lastHeartBeatTime > checkOutTimeInterval)
      //遍历
      for (c <- outTimeWorkerInfoes) {
        //获取超时的workerinfo对应的workerId
        val workerId: String = c.workerId
        //从map集合移除掉超时的worker信息
        id2WorkerInfoMap.remove(workerId)
        //从list集合移除掉超时的worker信息
        workerInfoList -= c
        println(s"workerId:$workerId 已经超时")
      }
      println("活着的worker总数:" + workerInfoList.size)
      // 按照worker内存大小降序排列
      println(workerInfoList.sortBy(x => x.memory).reverse)


    }
  }
}

object Master {
  def main(args: Array[String]): Unit = {

    val host = args(0)
    val port = args(1)
    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 masterActorSystem = ActorSystem.apply("masterActorSystem", config)

    masterActorSystem.actorOf(Props(new Master), "masterActor")

  }
}

Worker类

import java.util.UUID

import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
import com.typesafe.config.{Config, ConfigFactory}

import scala.concurrent.duration._

class Worker(memory:Int,cores:Int,masterHost:String,val masterPost:Int) extends Actor{
  //定义workId
  private val workerId=UUID.randomUUID().toString
  //定义worker定时发送心跳的时间间隔
  val sendHeartBeatInterval=10000//10秒

  var master: ActorSelection=_
  override def preStart(): Unit = {
    //获取master引用
    master=context.actorSelection(s"akka.tcp://masterActorSystem@$masterHost:$masterPost/user/masterActor")
    //向master发送注册信息,通过样例类封装注册信息(workId,memory,cores)
    master ! registMessage(workerId,memory,cores)


  }

  override def receive: Receive = {
    case RegisteredMessage(message)=>{
      println(message)
      //向master定时发送心跳  由于master的引用类型跟所需要的参数不一致,这里不可以直接向master发送信息,使用self(表示worker自己本身)
      //手动导入隐式转换
      import context.dispatcher
      context.system.scheduler.schedule(0 millis, sendHeartBeatInterval millis, self, SendHeartBeat)
    }
    //worker接受worker的发送信息
    case SendHeartBeat =>{
      //worker真正向master发送心跳
      master !  HeartBeat(workerId)
    }
  }
}

object Worker{
  def main(args: Array[String]): Unit = {
    val host=args(0)
    val port=args(1)
    val memory=args(2).toInt
    val cores=args(3).toInt
    val masterHost=args(4)
    val masterPost=args(5).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: Config = ConfigFactory.parseString(configStr)

    val workerActorSystem = ActorSystem.apply("WorkerActorSystem",config)

     workerActorSystem.actorOf(Props(new Worker(memory,cores,masterHost,masterPost)),"workerActor")
  }
}

样例类

class Messages extends Serializable{

}
//worker向master发送注册信息
case class  registMessage(workerId:String,memory:Int,cores:Int) extends Messages

//master向worker返回注册成功信息
case class RegisteredMessage(massage:String)extends Messages

//worker向自己发送信息
case object SendHeartBeat

//worker向master发送心跳
case class HeartBeat(workerId:String) extends Messages

//master向naster自己发送消息 ,由于在同一进程中,不需要实现序列化
case object CheckOutTime

WorkerInfo类

class WorkerInfo(val workerId:String,val memory:Int,val cores:Int) {
  //用于存储worker的上次心跳时间
  var lastHeartBeatTime:Long=_

  override def toString: String = {
    "workerId:"+workerId+" memory"+memory+" cores"+cores
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值