scala---- Akka-RPC通信案例框架

 RPC通信框架

    1. 启动Master,然后内部会启动一个定时器,定期检测超时的worker,将超时的worker删除
    2. Master接收到了worker发送的注册信息 , 将信息保存到内存,也可以持久化到磁盘或者zooker
    3. 启动worker,跟Master简历网络连接,将自己的信息(id,内存,cpu等信息)注册给Master
    4. Master向worker发送一个注册成功的消息
    5. Worker接收到Master的消息后,然后启动一个定时器,向Master定期发送心跳消息,发送心跳的消息是为了报活

运行时传入参数(右上角锤子旁边的 Edit Configurations)  列如: Master: localhost 8888   Worker: localhost 8888 localhost 9999 4

也可以打包在windows上或者linux上执行

本地:   java -cp 包名 master的全路径 localhost 8888         

           java -cp 包名 worker的全路径 localhost 8888 localhost 9999 4096 4

linux:    java -cp 包名 master的全路径 192.168.133.3 8888

             java -cp 包名 worker的全路径 192.168.133.3  8888  192.168.133.4  9999 4096 4

package cn.doit.rpc

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

/*
    1. 启动Master,然后内部会启动一个定时器,定期检测超时的worker,将超时的worker删除
    2. Master接收到了worker发送的注册信息 , 将信息保存到内存,也可以持久化到磁盘或者zooker
    3. 启动worker,跟Master建立网络连接,将自己的信息(id,内存,cpu等信息)注册给Master
    4. Master向worker发送一个注册成功的消息
    5. Worker接收到Master的消息后,然后启动一个定时器,向Master定期发送心跳消息,发送心跳的消息是为了报活
 */

//要使Master编程一个Actor 需要它实现Actor接口
class Master extends Actor{

  val CHECK_INTERVAL = 15000
  //将接收到的worker的信息保存在一个map集合中 一worker的id为键
  val idToWorker = new mutable.HashMap[String, WorkerInfo]()

  //类加载之后,接收消息之前开启节点检测 每隔一段时间进行检测,并剔除连接超时的节点
  override def preStart(): Unit = {
    import context.dispatcher
    //注册好之后向自己发送开始检测的信号,每隔15秒执行一次
    context.system.scheduler.schedule(0.millisecond, CHECK_INTERVAL.millisecond, self, CheckTimeOutWorker)
  }

  //用来接收消息的
  override def receive: Receive = {
    //接收到开始检查超时节点的消息
    case CheckTimeOutWorker => {
      //过滤 将连接超时的节点提取出来
      val deadWorkers: Iterable[WorkerInfo] = idToWorker.values.filter(w => System.currentTimeMillis() - w.lastHeartBeatTime > 20000)
      //遍历
      deadWorkers.foreach(dw => {
        //将超时的节点剔除
        idToWorker -= dw.workerId
      })
      //打印当前正在工作的节点的个数
      println(s"current alive worker size: ${idToWorker.size}")
    }

    //接收来自worked的消息
    case RegisterWorker(workerId, memory, cores) => {
      println(s"workerId: $workerId, memory: $memory , cores: $cores")
      //将接收到的消息保存在集合中
      val workerInfo = new WorkerInfo(workerId, memory, cores)
      workerInfo.lastHeartBeatTime = System.currentTimeMillis()
      //将信息保存到内存中
      idToWorker(workerId) = workerInfo
      //返回Worker一个注册成功的消息
      sender() ! RegisteredWorker
    }

    //接收来自worker的心跳
    case HeartBeat(workerId) => {
      //查看该id是否注册,注册则取出对应的WorkerInfo
      if (idToWorker.contains(workerId)) {
        //取出workerInfo
        val workerInfo = idToWorker(workerId)
        //更新上一次心跳的时间
        workerInfo.lastHeartBeatTime = System.currentTimeMillis()
      }
    }
  }
}


object Master {

  val MASTER_ACTOR_SYSTEM_NAME = "MasterActorSystem"
  val MASTER_ACTOR_NAME = "MasterActor"

  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)
    //创建一个ActorSystem(单例的)
    val masterActorSystem = ActorSystem(MASTER_ACTOR_SYSTEM_NAME, config)

    //使用ActorSystem创建Actor
    masterActorSystem.actorOf(Props[Master], MASTER_ACTOR_NAME)
  }
}

package cn.doit.rpc

import java.util.UUID
import java.util.concurrent.TimeUnit
import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._

class Worker(val masterHost:String, val masterPort:String, val workerMemory:Int, val workerCores:Int) extends Actor {
  val HEARTBEAT_INTERVAL = 10000
  //与master的连接,设置为成员变量,获取一次复制后其他都可以直接使用
  var masterRef: ActorSelection = _
  //创建属于worker的自己的id
  var workerId = UUID.randomUUID().toString

  //在构造方法之后,receive之前执行一定会调用一次
  override def preStart(): Unit = {
    //与Master建立连接
    masterRef = context.actorSelection(s"akka.tcp://${Master.MASTER_ACTOR_SYSTEM_NAME}@$masterHost:$masterPort/user/${Master.MASTER_ACTOR_NAME}")
    //给master发送消息
    masterRef ! RegisterWorker(workerId, workerMemory, workerCores)
  }

  //用来接收消息的
  override def receive: Receive = {
    //接收到自己发送的心跳 在里面进行判断 之后发送心跳到master
    case SendHeartBeat => {
      //      if (){
      //
      //      }
      //向Master发送心跳
      masterRef ! HeartBeat(workerId)
    }

      //接收master返回的注册成功的消息
    case RegisteredWorker => {
      //注册成功向master定时发送心跳
      //启动一个定时器 scheduler调度器       延迟0秒,时长10秒
      import context.dispatcher
      context.system.scheduler.schedule(Duration(0, TimeUnit.MILLISECONDS), HEARTBEAT_INTERVAL.millisecond, self, SendHeartBeat)
    }
  }
}


  object Worker {

    val WORKER_ACTOR_SYSTEM_NAME = "WorkerActorSystem"
    val WORKER_ACTOR_NAME = "WorkerActor"

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

      val masterHost = args(0)
      val masterPort = args(1)

      val workerHost = args(2)
      val workerPort = args(3)

      val workerMemory = args(4).toInt
      val workerCores = args(5).toInt

      //设置配置文件的信息
      val host = "localhost"
      val port = 9999

      val configStr =
        s"""
           |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
           |akka.remote.netty.tcp.hostname = $workerHost
           |akka.remote.netty.tcp.port = $workerPort
           |""".stripMargin
      //获取配置信息对象
      val config = ConfigFactory.parseString(configStr)
      //创建一个ActorSystem(单例)
      val workerActorSystem = ActorSystem(WORKER_ACTOR_SYSTEM_NAME, config)
      //通过ActorSystem创建Actor
      workerActorSystem.actorOf(Props(new Worker(masterHost,masterPort,workerMemory,workerCores)), WORKER_ACTOR_NAME)
    }
  }



package cn.doit.rpc

//检查超时时间的
case object CheckTimeOutWorker

//发送心跳的
case class HeartBeat(workerId: String)

//Master返回给Worker的注册成功的消息
case object RegisteredWorker

//worker中自己给自己发送信息,字节给自己发麻烦,需要转接一下
case object SendHeartBeat

//给master发送消息
case class RegisterWorker(workerId: String, memory: Int, cores: Int)
package cn.doit.rpc
//用来封装worker数据的
class WorkerInfo(val workerId:String, var memory:Int, var cores:Int) {

  //最后一次发送心跳的时间
  var lastHeartBeatTime: Long = _
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值