scala案例akka-rpc通信

master

package com.doit.demo02

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

import scala.collection.mutable
import scala.concurrent.duration._
/**
  * @author 向阳木
  * @date 2020/09/14/ 17:36
  * @Description:
  *              Master客户端  接受并保存Worker的信息 启动定时器,检测Worker的心跳机制,并移除超时Worker
  */
class Master extends Actor{
  //将数据封装到map中
  val idToWorkerMap = new mutable.HashMap[String,WorkerInfo]()

  //重写preStart 启动定时器
  override def preStart(): Unit = {
    //启动定时器,定期检测
    /**
      * 第一个参数 自动启动的时间
      * 第二个参数 检测的时间的间隔
      * 第三个参数 发给自身做中转转发
      * 第四个参数  worker时间和本地时间差
      */
    import context.dispatcher
    context.system.scheduler.schedule(0.milliseconds , 15000.milliseconds , self ,CheckTimeOutWorker  )
  }
  //接受消息
  //
  override def receive: Receive = {
    // 接受worker的注册信息
    case RegisterWorker(workerId,memory,cores) => {
      //接受传过来的数据,并将数据封装
      val workerInfo = new WorkerInfo(workerId, memory, cores)
      if (!idToWorkerMap.contains(workerId)) {
        idToWorkerMap.put(workerId,workerInfo)
      }
      //向worker发送注册成功的消息
      sender() ! RegisteredWorker
    }

      //接受Worker 周期性心跳信息 worker 发来的消息携带的只有workerID 信息
      //然后根据当前的时间来给心跳时间赋值
    case HearBeat(workerId) => {
      //根据workerId 到map中找对应的workerInfo ,然后更新最新一次心跳时间
      val workerInfo: WorkerInfo = idToWorkerMap(workerId)
      //获取当前时间
      val currenTime: Long = System.currentTimeMillis()
      //更新workerInfo 的时间
      workerInfo.lastHearBeatTime = currenTime
    }

      //匹配检测超时的信息
       //mater 每15s 给自己发送一次
    case CheckTimeOutWorker =>{
      //检测所有超时的worker
      val deadWorkers: Iterable[WorkerInfo] = idToWorkerMap.values.filter(w => System.currentTimeMillis() - w.lastHearBeatTime > 10000)
      //移除超时的worker
      deadWorkers.foreach(w =>{
        //从map中移除
        idToWorkerMap.remove(w.workerId)
      })
      //打印此时正在工作的worker的数量
      println(s"current alive worker size is ${idToWorkerMap.size}")

    }

  }
}
object Master{
  val MASTER_ACTOR_SYSTEM = "MASTER_ACTOR_SYSTEM"
  val MASTER_ACTOR = "MASTER_ACTOR"

  def main(args: Array[String]): Unit = {
   /// val masterHost = args(0)
    val masterHost = "localhost"
  //  val masterPort = args(1).toInt
    val masterPort = 8888

    val configStr = s"""
                       |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
                       |akka.remote.netty.tcp.hostname = $masterHost
                       |akka.remote.netty.tcp.port = $masterPort
                       |""".stripMargin

    val config = ConfigFactory.parseString(configStr)

    //先创建actorSystem 单例
    val system: ActorSystem = ActorSystem(MASTER_ACTOR_SYSTEM , config)
    //创建actor
    system.actorOf(Props[Master] , MASTER_ACTOR)
  }



}

woker

package com.doit.demo02

import java.util.UUID

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

import scala.concurrent.duration._

/**
  * @author 向阳木
  * @date 2020/09/14/ 17:40
  * @Description: Worker 客户端
  *                    和Master建立连接,并向其发送注册信息  建立定时器,定期向Master汇报自身信息,并发送心跳
  */
class Worker(val masterHost:String , val masterPort:Int , var workerMemory:Int , var workerCores:Int) extends Actor{
  //定义 workerId 保证唯一性
  val workerId = UUID.randomUUID().toString
// 将此变量定义为全局变量
  var masterRef: ActorSelection = null
  //重写preStart方法  在receive 方法之前执行一次
  override def preStart(): Unit = {
    //和master建立连接(拿到master的连接)
   masterRef = context.actorSelection(s"akka.tcp://${Master.MASTER_ACTOR_SYSTEM}@$masterHost:$masterPort/user/${Master.MASTER_ACTOR}")
    //向master发送消息
    masterRef ! RegisterWorker(workerId , workerMemory ,workerCores)
  }

  //接收master发来的消息
  override def receive: Receive = {
    //从master发来的 注册成功的信息
    case RegisteredWorker =>{
      //接收注册成功的信息后 在worker内部开启一个定时器,定期向Mater发送心跳
      //导入隐式转换
      import context.dispatcher
      context.system.scheduler.schedule(0.milliseconds , 10000.milliseconds , self , SendHearBeat)
    }
    case SendHearBeat =>{
      //用if 判断逻辑
      //向master 发送心跳
      masterRef ! HearBeat(workerId)
    }

  }
}
object Worker{
  // 定义worker system名 和 actor的名
  val WORKER_ACTOR_SYSTEM = "WORKER_ACTOR_SYSTEM"
  val WORKER_ACTOR = "WORKER_ACTOR"

  def main(args: Array[String]): Unit = {
      //接收参数
    /*val masterHost = args(0)
    val masterPort = args(1).toInt
    val workerHost = args(2)
    val workerPort = args(3).toInt
    val workerMemory = args(4).toInt
    val workerCores = args(5).toInt*/

    val masterHost = "localhost"
    val masterPort = 8888
    val workerHost = "localhost"
    val workerPort = 9999
    val workerMemory = 4086
    val workerCores = 8

    // 定义 configStr 的变量
    val configStr =
      s"""
         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
         |akka.remote.netty.tcp.hostname = $workerHost
         |akka.remote.netty.tcp.port = $workerPort
         |""".stripMargin
    //获得config
    val config: Config = ConfigFactory.parseString(configStr)
      //获得actorSystem
    val system: ActorSystem = ActorSystem(WORKER_ACTOR_SYSTEM , config)
    //获得actor
    system.actorOf(Props(new Worker(masterHost , masterPort , workerMemory ,workerCores)) , WORKER_ACTOR)

  }



}

workerInfo

package com.doit.demo02

/**
  * @author 向阳木
  * @date 2020/09/14/ 17:48
  * @Description:
  *              封装worker数据
  */
class WorkerInfo(val workerId:String , var memory:Int , var cores:Int) {

  var lastHearBeatTime:Long = _

}

Messages

package com.doit.demo02
/**
  * @author 向阳木
  * @date 2020/09/14/ 17:58
  * @Description: 封装master和worker 以及自身内部的通信数据
  */
//Master发给自己的检测信息
case object CheckTimeOutWorker

//worker 发送给Master的注册消息
case class RegisterWorker(workerId:String , memory:Int , cores:Int)

//master向worker发送注册成功的信息
//case object 没有参数输入 是一个多例的
case object RegisteredWorker

//worker 发送给 master的心跳消息
case class HearBeat(workerId:String)

//worker 发送给master的心跳信息 内部中间转发给自身的信息
case object SendHearBeat
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值