大数据学习day17------第三阶段-----scala05------1.Akka RPC通信案例改造和部署在多台机器上 2. 柯里化方法

假装没事ソ 提交于 2019-11-27 05:38:05

1.Akka RPC通信案例改造和部署在多台机器上

 1.1 Akka RPC通信案例的改造(主要是把一些参数不写是)

 Master

package com._51doit.akka.rpc

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

import scala.concurrent.duration._
import scala.collection.mutable

// Actor编程模型进行通信,需要让其与AKKA发生点关系(此处实现Actor特质)
class Master extends Actor {
  // 定义一个map,用来接收数据
  val id2Worker = new mutable.HashMap[String, WorkerInfo]()

  // 在preStart中启动定时器,定期检查超超时的Worker,然后剔除
  override def preStart(): Unit = {
    import context.dispatcher

    context.system.scheduler.schedule(0 millisecond, 10000 millisecond, self, CheckTimeOutWorker)
  }

  override def receive: Receive = {
    // Master匹配并接收Worker发送过来的注册消息
    case RegisterWorker(id, memory, cores) => {
      //将数据封装起来,保存到内存中
      val workerInfo: WorkerInfo = new WorkerInfo(id, memory, cores)
      id2Worker(id) = workerInfo
      //向Worker反馈一个注册成功的消息
      sender() ! RegisteredWorker
    }
    // Master匹配并接收Worker发送过来的心跳汇报消息
    case Heartbeat(workerId) => {
      // 根据workId去map中查找相对应的WorkerInfo
      if (id2Worker.contains(workerId)) {
        //根据ID取出WorkerInfo
        val workerInfo = id2Worker(workerId)
        //获取当前时间
        val currentTime = System.currentTimeMillis()
        //更新最近一次心跳时间
        workerInfo.lastUpdateTime = currentTime
      }
    }
    // 匹配定时器发送的内容,用于提出超时的worker
    case CheckTimeOutWorker => {
      val currentTime = System.currentTimeMillis()
      // 取出map中的值,并计算出超时的worker
      val values: Iterable[WorkerInfo] = id2Worker.values
      val deadWorkers: Iterable[WorkerInfo] = values.filter(value => currentTime - value.lastUpdateTime > 10000)
      // 移除所有超时的worker
      deadWorkers.foreach(dw => id2Worker -= dw.id)
      println("current alive worker is : " + id2Worker.size)
    }
  }
}

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

  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 conf = ConfigFactory.parseString(configStr)
    val actorSystem = ActorSystem.apply(MASTER_ACTOR_SYSTEM, conf)
    // 通过ActorSystem对象创建Actor(通过反射指定特定类型的Actor实例)
    val masterActor: ActorRef = actorSystem.actorOf(Props[Master], name = MASTER_ACTOR)

  }
}
View Code

Worker

package com._51doit.akka.rpc

import java.util.UUID

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

/**
 * Worker Actor最好在构造方法执行之后,receive方法之前,向Master建立连接
 */
class Worker(val masterHost: String, val masterPort: Int, val memory: Int, val cores: Int) extends Actor {
  var masterRef: ActorSelection = _

  val WORKER_ID: String = UUID.randomUUID().toString
  val HEARTBEAT_INTERVAL: Int = 5000
  // 生命周期方法(一定并且按一定顺序执行的方法)
  // 在构造方法之后,receive方法之前,执行一次preStart
  override def preStart(): Unit = {
    // Worker向Master建立网络连接,得到一个master代理对象
    masterRef = context.actorSelection(s"akka.tcp://${Master.MASTER_ACTOR_SYSTEM}@$masterHost:$masterPort/user/${Master.MASTER_ACTOR}")
    println(s"akka.tcp://${Master.MASTER_ACTOR_SYSTEM}@masterHost:masterPort/user/${Master.MASTER_ACTOR}")
    // //Worker向Master发送注册的信息
    masterRef ! RegisterWorker(WORKER_ID, memory, cores)
}
  // 重写用于接收消息的方法
  override def receive: Receive = {
    //Master反馈给Worker的消息
    case RegisteredWorker => {

      //导入隐式转换
      import context.dispatcher
      //启动一个定时器,定期向Master发送心跳,使用Akka框架封装的定时器
      //定期给自己发送消息,然后再给Master发送心跳
      //参数依次为第一次的延迟时间,多少时间执行一次,消息发送给谁(此处找不到master,发送给masterRef代理对象也不行,),发送的消息
      context.system.scheduler.schedule(0 millisecond, HEARTBEAT_INTERVAL millisecond, self, SendHeartbeat)
    }
    //自己给自己发送的消息
    case SendHeartbeat => {

      //可以进行一些逻辑判断
      //向Master发送心跳消息
      masterRef ! Heartbeat(WORKER_ID)
    }
  }
}

object Worker{
  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 memory = args(4).toInt
    val cores = args(5).toInt
    val configStr =
      s"""
         |akka.actor.provider = "akka.remote.RemoteActorRefProvider" // 负责通信的核心类,有必要可以自己定义
         |akka.remote.netty.tcp.hostname = "$workerHost"
         |akka.remote.netty.tcp.port = "$workerPort"
      """.stripMargin  // 此方法负责切割
    val conf = ConfigFactory.parseString(configStr)
    val workerActorSystem = ActorSystem(WORKER_ACTOR_SYSTEM, conf)
    val workerActor: ActorRef = workerActorSystem.actorOf(Props(new Worker(masterHost,masterPort,memory,cores)), name = WORKER_ACTOR)

  }
}
View Code

此处自己犯的错误:创建与master的连接时忘了加符号(s   $), 以下是自己写的

"akka.tcp://${Master.MASTER_ACTOR_SYSTEM}@masterHost:masterPort/user/${Master.MASTER_ACTOR}"

正确的

 masterRef = context.actorSelection(s"akka.tcp://${Master.MASTER_ACTOR_SYSTEM}@$masterHost:$masterPort/user/${Master.MASTER_ACTOR}")

1.2 将上述代码部署到多台机器上

 1.2.1  打包

 (1)第一种方法:指定main方法的形式

 在pom.xml文件中指定,如下

 

 

 

 打包步骤:直接双击如下图中的package即可

 

 

akka-rpc-1.0-SNAPSHOT.jar中是既包括了自己写的代码,也包括了需要的一些依赖包(正是我们需要的jar包),而另一个jar包只包括自己写的代码

 打包的包名如上表所示是pom.xml中定义了(如下图),默认是打的包为jar

 

 

 由于一个面方法只能运行一次,但worker和master都要运行,所以需要将pom.xml文件中的main方法分别改为worker和master进行打包,这正是这种方法的缺点,麻烦

 分别将jar包拖至桌面,运行命令以及结果如下

(2)第二种方法:不指定main方法的形式(pom文件中不指定main方法)

打包形式一样,只是运行的命令变了,运行命令和运行结果如下:

 

 

 这样便能部署到多台机器上去(此处自己没有用多台机器,但只要ip改变下就行,)

 

 2. 柯里化方法

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!