11.大数据技术之Scala_十八章:并发编程Akka(终)

十八 并发编程Akka

18.1 Akka 介绍

1) Akka 是 JAVA 虚拟机 JVM 平台上构建高并发、分布式和容错应用的工具包和运行时,你可以 理解成 Akka 是编写并发程序的框架。

2) Akka 用 Scala 语言写成,同时提供了 Scala 和 JAVA 的开发接口。

3) Akka 主要解决的问题是:可以轻松的写出高效稳定的并发程序,程序员不再过多的考虑线程、锁和资源竞争等细节。

18.2 Actor 模型用于解决什么问题

1) 处理并发问题关键是要保证共享数据的一致性和正确性,因为程序是多线程时,多个线程对同 一个数据进行修改,若不加同步条件,势必会造成数据污染。但是当我们对关键代码加入同步条件synchronized 后,实际上大并发就会阻塞在这段代码,对程序效率有很大影响。

2) 若是用单线程处理,不会有数据一致性的问题,但是系统的性能又不能保证。

3) Actor 模型的出现解决了这个问题,简化并发编程,提升程序性能。 你可以这里理解:Actor 模型是一种处理并发问题的解决方案

Actor 模型及其说明

1) Akka 处理并发的方法基于 Actor 模型。

2) 在基于 Actor 的系统里,所有的事物都是 Actor,就好像在面向对象设计里面所有的事物都是对象一样。

3) Actor 模型是作为一个并发模型设计和架构的。Actor 与 Actor 之间只能通过消息通信

4) Actor 与 Actor 之间只能用消息进行通信,当一个 Actor 给另外一个 Actor 发消息,消息是有顺序的(消息队列),只需要将消息投寄的相应的邮箱即可。

5) 怎么处理消息是由接收消息的 Actor 决定的,发送消息 Actor 可以等待回复,也可以异步处理【ajax】

6) ActorSystem 的职责是负责创建并管理其创建的 Actor, ActorSystem 是单例的(可以ActorSystem 是一个工厂,专门创建 Actor),一个 JVM 进程中有一个即可,而 Acotr 是可以有多个的。

7) Actor 模型是对并发模型进行了更高的抽象。

8) Actor 模型是 异步、 非阻塞 、 高性能的事件驱动编程模型。

9) Actor 模型是轻量级事件处理(1GB 内存可容纳百万级别个 Actor),因此处理大并发性能高.

18.3 Actor工作机制

 

1) ActorySystem 创建 Actor

2) ActorRef:可以理解成是Actor的代理或者引用。消息是通过ActorRef来发送,而不能通过Actor 发送消息,通过哪个 ActorRef 发消息,就表示把该消息发给哪个Actor

3) 消息发送到 Dispatcher Message (消息分发器),它得到消息后,会将消息进行分发到对应的MailBox。(注: Dispatcher Message 可以理解成是一个线程池, MailBox 可以理解成是消息队列,可以缓冲多个消息,遵守 FIFO)

4) Actor 可以通过 receive 方法来获取消息,然后进行处理。

18.4 基本案例

HelloWorld

import akka.actor.{Actor, ActorRef, ActorSystem, Props}

 

class SayHelloActor extends Actor{

  override def receive: Receive = {

    case "hello" => println("收到hello")

    case "ok" => println("收到ok")

    case "exit" => {

      println("收到exit")

      context.stop(self)

      context.system.terminate()//退出系统

    }

    case _ => println("匹配不到")

  }

}

 

object SayHelloActor extends App {

  //先创建一个ActorSystem,专门用于创建Actor

  private val actoryFactory = ActorSystem("actoryFactory")

 

 

  private val sayHelloActor: ActorRef = actoryFactory.actorOf(Props[SayHelloActor],"SayHelloActor")

 

  sayHelloActor ! "hello1"

}

 

两个Actor的通讯

import akka.actor.{Actor, ActorRef, ActorSystem, Props}

 

class AActor(actorRef: ActorRef) extends Actor{

 

  val bActorRef:ActorRef = actorRef

 

  override def receive: Receive = {

    case "start" => {

      println("AActor,start OK")

      self ! "我打"

    }

 

    case "我打" => {

      println("我打架很厉害")

      Thread.sleep(1000)

      bActorRef ! "我打"

    }

  }

}

 

class BActor extends Actor{

  override def receive: Receive = {

    case "我打" => {

      println("BActor 我打启动")

      Thread.sleep(1000)

      sender() ! "我打"

    }

  }

}

 

object ActorGame extends App{

  private val actorFactory = ActorSystem("actorFactory")

 

  //创建Bactor代理

  private val bActorRef: ActorRef = actorFactory.actorOf(Props[BActor],"bActor")

  private val aActorRef: ActorRef = actorFactory.actorOf(Props(new AActor(bActorRef)),"aActor")

 

  aActorRef ! "start"

}

Master 和 Work通信

RegisterWorkerInfo 

//worker注册信息

case class RegisterWorkerInfo(id: String, cpu: Int, ram: Int)

 

//这个是WorkInfo,这个信息将来是保存到master的hm(该hashmap)是用于管理worker

//将来workerInfo会扩展

class WorkerInfo(val id: String, val cpu: Int, val ram: Int)

 

//当worker对象注册成功,服务器返回一个对象

case object RegisteredWorkerInfo

 

SparkMaster

import akka.actor.{Actor, ActorSystem, Props}

import com.typesafe.config.ConfigFactory

 

import scala.collection.mutable

 

class SparkMaster extends Actor{

  val workers = mutable.Map[String,WorkerInfo]()

 

  override def receive: Receive = {

    case "start" => println("master服务器启动了")

 

      //注册worker信息

    case RegisterWorkerInfo(id,cpu,ram) =>{

      //如果不包含就注册

      if(!workers.contains(id)){

        //创建WorkerInfo对象

        val  workerInfo = new WorkerInfo(id,cpu,ram)

 

        //加入workers

        workers += ((id,workerInfo))

 

        println("服务器worker=" + workers)

 

        //回复消息,注册成功

        sender() ! RegisteredWorkerInfo

      }

    }

  }

}

 

object SparkMaster {

  def main(args: Array[String]): Unit = {

    val config = ConfigFactory.parseString(

      s"""

        |akka.actor.provider="akka.remote.RemoteActorRefProvider"

        |akka.remote.netty.tcp.hostname=127.0.0.1

        |akka.remote.netty.tcp.port=10005

        |""".stripMargin

    )

 

    //先创建ActorSystem

    val sparkMasterSystem = ActorSystem("SparkMaster",config)

 

    //创建SpakrMaster - actor

    val actorRef = sparkMasterSystem.actorOf(Props[SparkMaster],"SparkMaster-01")

 

    //启动SparkMaster

    actorRef ! "start"

 

  }

}

SparkWorker

import akka.actor.{Actor, ActorSelection, ActorSystem, Props}

import com.typesafe.config.ConfigFactory

 

class SparkWorker(masterHost: String, masterPort: Int) extends Actor {

 

  var masterPorxy: ActorSelection = _

  val id = java.util.UUID.randomUUID().toString

 

 

  override def preStart(): Unit = {

    println("preStart()调用")

 

    //初始化masterPorxy

    masterPorxy =

      context.actorSelection(s"akka.tcp://SparkMaster@${masterHost}:${masterPort}/user/SparkMaster-01")

 

    println("masterProxy=" + masterPorxy)

  }

 

  override def receive: Receive = {

    case "start" => {

      println("worker启动了")

      //发出注册消息

      masterPorxy ! RegisterWorkerInfo(id, 16, 16 * 1024)

    }

 

    case RegisteredWorkerInfo => {

      println("workerid=" + id + "注册成功")

    }

  }

}

 

object SparkWorker{

  def main(args: Array[String]): Unit = {

    val workerHost = "127.0.0.1"

    val workerPort = 10001

    val masterHost = "127.0.0.1"

    val masterPort = 10005

 

    val config = ConfigFactory.parseString(

      s"""

        |akka.actor.provider="akka.remote.RemoteActorRefProvider"

        |akka.remote.netty.tcp.hostname=${workerHost}

        |akka.remote.netty.tcp.port=${workerPort}

        |""".stripMargin

    )

 

    val sparkWorkerSystem = ActorSystem("SparkWorker",config)

 

    val sparkWorkerRef = sparkWorkerSystem.actorOf(Props(new SparkWorker(masterHost,masterPort)),"SparkWorker-01")

 

    //启动actor

    sparkWorkerRef ! "start"

  }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值