基本思路
Master:
package com.ycit.akka.rpc
import akka.actor.{Actor,ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
import scala.collection.mutable
/**
* @author jpf
* @date 2019/6/5 15:34
*/
class Master(val host:String,val port:Int) extends Actor {
private val idToWorker = new mutable.HashMap[String,WorkerInfo]()
private val workers = new mutable.HashSet[WorkerInfo]
//超时检查的间隔
val CHECK_INTERVAL = 15000
override def preStart(): Unit = {
//导入隐式转换
import context.dispatcher //使用timer太low了, 可以使用akka的, 使用定时器, 要导入这个包
context.system.scheduler.schedule(0 millis, CHECK_INTERVAL millis, self, CheckTimeOutWorker)
}
//用于接受消息
override def receive: Receive = {
case RegisterWorker(id,memory,cores) =>{
if(!idToWorker.contains(id)){
val workerInfo = new WorkerInfo(id,memory,cores)
idToWorker(id)=workerInfo
workers +=workerInfo
sender ! RegisteredWorker(s"akka.tcp://MasterSystem@$host:$port/user/Master")// 通知worker注册
}
}
case Heartbeat(workerId)=>{
if(idToWorker.contains(workerId)){
val workerInfo = idToWorker(workerId)
//报活
val currentTime = System.currentTimeMillis()
workerInfo.lastHeartbeatTime=currentTime
}
}
case CheckTimeOutWorker=>{
val currentTime = System.currentTimeMillis()
val tomove = workers.filter(x=>currentTime-x.lastHeartbeatTime>CHECK_INTERVAL)
for(wk<-tomove){
workers -=wk
idToWorker -=wk.id
}
println("当前存在的worker:"+" "+workers.size)
}
}
}
object Master{
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 config = ConfigFactory.parseString(configStr)
val masterSystem = ActorSystem("MasterSystem",config)
//创建Actor 起个名字
masterSystem.actorOf(Props(new Master(host,port)),"Master")
masterSystem.awaitTermination()//让进程等待先别结束
}
}
Worker:
package com.ycit.akka.rpc
import java.util.UUID
import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
import scala.language.postfixOps
/**
* @author jpf
* @date 2019/6/5 15:34
*/
class Worker(val masterHost:String,val masterPort:Int,val memory:Int,cores:Int) extends Actor {
val workId = UUID.randomUUID().toString
var master : ActorSelection = _
val HEART_INTERVAL = 10000
override def preStart(): Unit = {
//在master启动时会打印下面的那个协议, 可以先用这个做一个标志, 连接哪个master
//继承actor后会有一个context, 可以通过它来连接
master = context.actorSelection(s"akka.tcp://MasterSystem@$masterHost:$masterPort/user/Master")
master ! RegisterWorker(workId,memory,cores)
}
override def receive: Receive = {
//接受master的回应消息
case RegisteredWorker(masterPath)=>{
println(masterPath)
//启动定时器发送心跳
import context.dispatcher //引入隐式转换对象
context.system.scheduler.schedule(0 millis,HEART_INTERVAL millis,self,SendHeartbeat)
}
case SendHeartbeat =>{
println("send heartbeat to master")
master ! Heartbeat(workId)
}
}
}
object Worker{
def main(args: Array[String]): Unit = {
val host = "127.0.0.1"
val port = 9999
val masterHost = "127.0.0.1"
val masterPort = 8888
val memory = 1024
val cores = 2
// 准备配置
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老大,辅助创建和监控下面的Actor,他是单例的
val WorkerSystem = ActorSystem("WorkerSystem",config)
//创建Actor 起个名字
WorkerSystem.actorOf(Props(new Worker(masterHost,masterPort,memory,cores)),"Worker")
WorkerSystem.awaitTermination()//让进程等待先别结束
}
}
RemoteMessage (发送消息的样例类)
package com.ycit.akka.rpc
/**
* @author jpf
* @date 2019/6/5 15:34
*/
trait RemoteMessage extends Serializable
//Worker -> Master
case class RegisterWorker(id: String, memory: Int, cores: Int) extends RemoteMessage
case class Heartbeat(id: String) extends RemoteMessage
//Master -> Worker
case class RegisteredWorker(masterUrl: String) extends RemoteMessage
//Worker -> self
case object SendHeartbeat
// Master -> self
case object CheckTimeOutWorker
由于写Java写习惯了为RemoteMessage接口后面加个{ }导致RemoteMessage 里面的样例类不能被序列化(困扰了我好久)
WorkerInfo(实体类)
package com.ycit.akka.rpc
/**
* @author jpf
* @date 2019/6/5 15:34
*/
class WorkerInfo(val id: String, val memory: Int, val cores: Int) {
var lastHeartbeatTime : Long = _
}