簡介
- 解決高併發的問題,其實主要有兩種解決思路,其中一個就是使用鎖的概念,這是非常普遍的,但是當面對的業務量不斷增多,各種鎖就會非常多。導致很難維護,同時最重要的,使用鎖其實是一個降低了系統效率的方法,也就是由於上下文切換等等帶來的浪費。
- 而另一種方法就是使用ACTOR模型,那麼什麼是ACTOR模型呢,
請在另一篇blog中查看
當然其中不論是使用鎖、CAS,還是使用actor模型,都是各有利弊的。在不同的業務中有不同的優點。
但是在大數據的spark框架中,actor模型很普遍也很適合,有利於有向圖邏輯的計算。所以這次我分享一下scala寫的一個主從模型框架。
框架模型
首先明確需求:
- Master能夠監聽slaver的加入
- 同時Master能夠監聽slaver的故障
- Master擁有所有節點的信息
對一來說:
在Actor模型中,定義一個註冊Message,Slaver想要加入集羣的時候,發送註冊Msg給Master,然後如果符合要求,就加入到一個Maser的全局變量,這個全局變量也就解決了上述說的第三個需求。
對二來說:
既然要確認slaver的存活情況,就需要引入心跳機制,也就是說,每隔一段時間,每一個Slaver就需要向Master發送一個心跳信號,也就是一個Msg。如果在一段時間內Master收到了這個心跳信號,就知道了,這個Slaver是正常運行的,是存活的。那麼既然有存活的,就肯定有不能發送心跳信號的“死”的Slaver。
這個就需要Master定時檢測有沒有收到每一個已經註冊過的Slaver的心跳Msg,如果在TIMELIMIT這段時間內沒有收到,那麼就刪除這個節點的註冊信息。
這個時候這個時候就有這麼一個問題:
如何運行這個定時任務呢,實現的方法其實主要是兩種,第一個就是直接編寫一個循環的定時,當然可以實現,但是一般比較主流的方法就是給自己一個心跳。
自己 的心跳機制作爲定時的任務的觸發器。定時給自己發送Msg。
目錄結構
運行結果
首先我們啓動Master節點,
由圖可見,現在有0個Slaver
然後我們啓動一個Slaver
一段時間後,發現Master
已經註冊成功,然後我們當然可以繼續啓動Slaver,然後count會繼續增加爲2 ,
然後我們關掉Slaver,
然後Master會顯示count減少。
這樣就實現了我們的目的,在實際應用中我們可以實現集羣的管理,有向圖邏輯任務的管理等等。
代碼
slaverInfo
package day0208
/**
* @Author: Braylon
* @Date: 2020/2/8 12:14
* @Version: 1.0
*/
class SlaverInfo(var ID:String, var Host:String, var cores:String, var backup:String) {
var lastHeartBeat:Long = System.currentTimeMillis()
override def toString() = s"SlaverInfo:$ID, $Host, $cores, $backup"
}
Messages
package day0208
/**
* @Author: Braylon
* @Date: 2020/2/8 12:09
* @Version: 1.0
*/
//從節點信息
case class RegisterMsg(var ID:String ,var host:String, var cores:String, var backup:String)
//從節點觸發心跳信息
case class SelfHeartBeatMsg()
//從節點---》master 心跳信息
case class ToMasterHeartBeat(var ID:String)
//主節點信息
case class Master2SlaverACK()
//主節點自檢測節點存活信息
case class SelfCheckNodesState()
class Messages {
}
Master
package day0208
import akka.actor.{Actor, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.collection.mutable
/**
* @Author: Braylon
* @Date: 2020/2/8 12:09
* @Version: 1.0
*/
class Master extends Actor{
val slaversMap:mutable.HashMap[String, SlaverInfo] = new mutable.HashMap[String, SlaverInfo]()
val slaversSet:mutable.HashSet[SlaverInfo] = new mutable.HashSet[SlaverInfo]()
val TIMELIMIT = 10 * 1000
override def receive() = {
case RegisterMsg(id, host, cores, backup) => {
if (!slaversMap.contains(id)){
val new_slaverInfo = new SlaverInfo(id, host, cores, backup)
slaversSet.add(new_slaverInfo)
slaversMap(id) = new_slaverInfo
sender ! Master2SlaverACK
}
}
case ToMasterHeartBeat(id) => {
val slaverInfo_ = slaversMap(id)
println("get heartBeat from slaver:" + slaverInfo_)
slaverInfo_.lastHeartBeat = System.currentTimeMillis()
}
case SelfCheckNodesState => {
val curTimestamp = System.currentTimeMillis()
val outRecord = slaversSet.filter(x => curTimestamp - x.lastHeartBeat > TIMELIMIT)
for (item <- outRecord){
slaversSet -= item
slaversMap.remove(item.ID)
}
println("slavers count :" + slaversSet.size)
}
}
override def preStart(): Unit = {
context.system.scheduler.schedule(5 millis, TIMELIMIT milli, self, SelfCheckNodesState)
}
}
object Master{
def main(args: Array[String]): Unit = {
val host:String = "localhost"
val port = 9999
val confStr =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
val config = ConfigFactory.parseString(confStr)
val actorSys = ActorSystem.create("MasterNode", config)
actorSys.actorOf(Props[Master],"Master")
}
}
Slaver
package day0208
import java.util.UUID
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import akka.actor._
import com.typesafe.config.ConfigFactory
/**
* @Author: Braylon
* @Date: 2020/2/8 12:09
* @Version: 1.0
*/
class Slaver extends Actor{
val ID : String = UUID.randomUUID().toString
var master : ActorSelection = null
override def preStart(): Unit = {
//創建連接
master = context.system.actorSelection("akka.tcp://MasterNode@localhost:9999/user/Master")
//發送註冊
master ! RegisterMsg(ID,"localhost","16","無備註")
}
override def receive() = {
case Master2SlaverACK => {
context.system.scheduler.schedule(0 millis, 5000 millis, self, SelfHeartBeatMsg)
}
case SelfHeartBeatMsg => {
println("slaver self heartBeat")
master ! ToMasterHeartBeat(ID)
}
}
}
object Slaver{
def main(args: Array[String]): Unit = {
var slaverPort = 9990
var confStr =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.port = $slaverPort
""".stripMargin
var config = ConfigFactory.parseString(confStr)
var actorSys = ActorSystem.create("SlaverNode",config)
actorSys.actorOf(Props[Slaver],"Slaver")
}
}
願身體健康
大家共勉~~