大數據學習(十七)scala實現actor模型管理系統

簡介

  • 解決高併發的問題,其實主要有兩種解決思路,其中一個就是使用鎖的概念,這是非常普遍的,但是當面對的業務量不斷增多,各種鎖就會非常多。導致很難維護,同時最重要的,使用鎖其實是一個降低了系統效率的方法,也就是由於上下文切換等等帶來的浪費。
  • 而另一種方法就是使用ACTOR模型,那麼什麼是ACTOR模型呢,
    請在另一篇blog中查看
    當然其中不論是使用鎖、CAS,還是使用actor模型,都是各有利弊的。在不同的業務中有不同的優點。
    但是在大數據的spark框架中,actor模型很普遍也很適合,有利於有向圖邏輯的計算。所以這次我分享一下scala寫的一個主從模型框架。

框架模型

在這裏插入圖片描述
首先明確需求:

  1. Master能夠監聽slaver的加入
  2. 同時Master能夠監聽slaver的故障
  3. 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")
  }
}

願身體健康

大家共勉~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章