基本思路
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 = _
}