Akka模拟Spark Master和Worker通信


学习Saprk的源代码,那么对Akka的通信原理一定要熟悉,我们通过Akka模拟Spark Master和Worker之间的通信过程,从而深入Spark的Master和Worker的通讯机制。学习Spark Master和Worker如何通信之前需要了解 Akka网络编程案例.

案例意义

  1. 深入理解Spark的Master和Worker的通讯机制。
  2. 深入理解Spark的底层源码。
  3. 了解Spark源码,方便对Spark进行二次开发

需求分析

Master
Worker
Worker
Worker
  1. Worker启动向Master注册,Worker在Master中注册成功后,回复Worker注册成功。
  2. Worker定时向Master发送心跳。
  3. Master接收到Worker的心跳后,更新Worker的最近一次心跳时间。
  4. Master启动定时任务,定时检测注册的Worker中有哪些没有更新心跳,如Worker没有更新心跳,将其从hashMap中删除。

Spark Master和Worker通信案例

Master代码

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

class SparkMaster extends Actor {
  //定义个hm,管理workers
  val workers = mutable.Map[String, WorkerInfo]()

  override def receive: Receive = {
    case "start" => {
      println("master服务器启动了...")
      //这里开始。。
      self ! StartTimeOutWorker
    }
    case RegisterWorkerInfo(id, cpu, ram) => {
      //接收到worker注册信息
      if (!workers.contains(id)) {
        //创建WorkerInfo 对象
        val workerInfo = new WorkerInfo(id, cpu, ram)
        //加入到workers
        workers += ((id, workerInfo))
        println("服务器的workers=" + workers)
        //回复一个消息,说注册成功
        sender() ! RegisteredWorkerInfo
      }
    }
    case HeartBeat(id) => {
      //更新对应的worker的心跳时间
      //1.从workers取出WorkerInfo
      val workerInfo = workers(id)
      workerInfo.lastHeartBeat = System.currentTimeMillis()
      println("master更新了 " + id + " 心跳时间...")
    }
    case StartTimeOutWorker => {
      println("开始了定时检测worker心跳的任务")
      import context.dispatcher
      //说明
      //1. 0 millis 不延时,立即执行定时器
      //2. 9000 millis 表示每隔9秒执行一次
      //3. self:表示发给自己
      //4. RemoveTimeOutWorker 发送的内容
      context.system.scheduler.schedule(0 millis, 9000 millis, self, RemoveTimeOutWorker)
    }
    //对RemoveTimeOutWorker消息处理
    //这里需求检测哪些worker心跳超时(now - lastHeartBeat > 6000),并从map中删除
    case RemoveTimeOutWorker => {
      //首先将所有的 workers 的 所有WorkerInfo
      val workerInfos = workers.values
      val nowTime = System.currentTimeMillis()
      //先把超时的所有workerInfo,删除即可
      workerInfos.filter(workerInfo => (nowTime - workerInfo.lastHeartBeat) > 6000)
        .foreach(workerInfo => workers.remove(workerInfo.id))
      println("当前有 " + workers.size + " 个worker存活的")
    }
  }
}

object SparkMaster {
  def main(args: Array[String]): Unit = {
    val host = "127.0.0.1"
    val port = 9999
    val name = "MasterRef"

    //先创建ActorSystem
    val config = ConfigFactory.parseString(
      s"""
         |akka.actor.provider="akka.remote.RemoteActorRefProvider"
         |akka.remote.netty.tcp.hostname=${host}
         |akka.remote.netty.tcp.port=${port}
            """.stripMargin)
    val sparkMasterSystem = ActorSystem("SparkMaster", config)
    //创建SparkMaster -actor
    val sparkMasterRef = sparkMasterSystem.actorOf(Props[SparkMaster], s"${name}")
    //启动SparkMaster
    sparkMasterRef ! "start"
  }
}

Worker代码

import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
import akka.sparkmasterworker.common.{HeartBeat, RegisterWorkerInfo, RegisteredWorkerInfo, SendHeartBeat}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._

class SparkWorker(masterHost:String,masterPort:Int,masterName:String) extends Actor{
  //masterProxy是Master的代理/引用ref
  var masterPorxy :ActorSelection = _
  val id = java.util.UUID.randomUUID().toString

  override def preStart(): Unit = {
    println("preStart()调用")
    //初始化masterPorxy
    masterPorxy = context.actorSelection(s"akka.tcp://SparkMaster@${masterHost}:${masterPort}/user/${masterName}")
    println("masterProxy=" + masterPorxy)
  }
  override def receive:Receive = {
    case "start" => {
      println("worker启动了")
      //发出一个注册消息RegisterWorkerInfo(id,cpu,ram)
      masterPorxy ! RegisterWorkerInfo(id, 16, 16 * 1024)
    }
    case RegisteredWorkerInfo => {
      println("workerid= " + id + " 注册成功~")
      //当注册成功后,就定义一个定时器,每隔一定时间,发送SendHeartBeat给自己
      import context.dispatcher
      //说明
      //1. 0 millis 不延时,立即执行定时器
      //2. 3000 millis 表示每隔3秒执行一次
      //3. self:表示发给自己,也即是获取到自己的ActorRef引用
      //4. SendHeartBeat 发送的内容
      context.system.scheduler.schedule(0 millis, 3000 millis, self, SendHeartBeat)
    }
    case SendHeartBeat =>{
      println("worker = " + id + "给master发送心跳")
      masterPorxy ! HeartBeat(id)
    }
  }
}

object SparkWorker {
  def main(args: Array[String]): Unit = {
    val workerHost = "127.0.0.1"
    val workerPort = 8889
    val workerName = "WorkerRef"
    val masterHost = "127.0.0.1"
    val masterPort = 9999
    val masterName = "MasterRef"
    
    val config = ConfigFactory.parseString(
      s"""
         |akka.actor.provider="akka.remote.RemoteActorRefProvider"
         |akka.remote.netty.tcp.hostname=${workerHost}
         |akka.remote.netty.tcp.port=${workerPort}
            """.stripMargin)
    //创建ActorSystem
    val sparkWorkerSystem = ActorSystem("SparkWorker",config)
    //创建SparkWorker 的引用/代理
    val sparkWorkerRef = sparkWorkerSystem.actorOf(Props(new SparkWorker(masterHost, masterPort.toInt,masterName)), s"${workerName}")
    //启动actor
    sparkWorkerRef ! "start"
  }
}
  • 说明
  1. 首先Master启动,然后Worker启动,Worker启动后就使用context.actorSelection()方法获取到MasterRef,然后开始向Master注册信息(RegisterWorkerInfo)。
  2. Master 接收到RegisterWorkerInfo通过hashMap添加Worker的信息 ,创建WorkerInfo 对象
    val workerInfo = new WorkerInfo(id, cpu, ram),将信息加入到HashMap中workers += ((id, workerInfo))。
  3. 添加完workerInfo后,Master向Worker已注册完成RegisteredWorkerInfo,Worker启用定时器,定时的向Master发送自己的心跳信息(masterPorxy ! HeartBeat(id))。
  4. Master根据Worker的id更新workerInfo的最新的心跳时间。
  5. Master启动时会定时的调用RemoveTimeOutWorker来检测超时没有及时更新心跳的Worker,把没有及时更新心跳Worker移除hashMap。

样例类

// worker注册信息 //MessageProtocol.scala
case class RegisterWorkerInfo(id: String, cpu: Int, ram: Int)

// 这个是WorkerInfo, 这个信息将来是保存到master的 hm(该hashmap是用于管理worker)
// 将来这个WorkerInfo会扩展(比如增加worker上一次的心跳时间)
class WorkerInfo(val id: String, val cpu: Int, val ram: Int) {
  var lastHeartBeat : Long = System.currentTimeMillis()
}

// 当worker注册成功,服务器返回一个RegisteredWorkerInfo 对象
case object RegisteredWorkerInfo
//worker每隔一定时间由定时器发给自己的一个消息
case object SendHeartBeat
//worker每隔一定时间由定时器触发,而向master发现的协议消息
case class HeartBeat(id: String)

//master给自己发送一个触发检查超时worker的信息
case object StartTimeOutWorker
// master给自己发消息,检测worker,对于心跳超时的.
case object RemoveTimeOutWorker

结果

  • Worker结果

在这里插入图片描述

  • Master 结果在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值