并发编程模型AKKA

  1. 1.AKKA简介

AKKA是JAVA虚拟机JVM平台上构建高并发,分布式和容错应用的工具包。也可以理解成scala语言编写的并发框架。这个应用在spark和flink集群中master和worker通信就是应用这个框架,因此应用还是比较广泛和重要的。因为AKKA不同版本通讯不了,用户使用AKKA和spark中AKKA冲突,spark放弃维护AKKA等原因,在spark2.x后全更改成netty写的了。但这个原理也是按这个原理来写的。

  • 2.AKKA的作用
    方便开发人员写出高效稳定的并发框架,不再过多的考虑线程,锁,网络通讯和资源竞争等细节。
  • 3.AKKA处理并发的方法基于 Actor 模型(如图)

并发编程模型AKKA_第1张图片3.1在基于 Actor 的系统里,所有的事物都是 Actor,就好像在面向对象设计里面所有的事物都是对象一样。Actor 模型是作为一个并发模型设计和架构的。
3.2Actor 与 Actor 之间只能通过消息通信,如图的信封。每个Actor他们都有自己的Mailbox与其绑定。绑定是由系统来处理的。
3.3Actor 与 Actor 之间只能用消息进行通信,当一个 Actor 给另外一个 Actor发消息,消息是有顺序的(消息队列),只需要将消息投寄的相应的邮箱即可。
3.4怎么处理消息是由接收消息的Actor决定的,发送消息Actor可以等待回复,也可以异步处理。
3.5ActorSystem 的职责是负责创建并管理其创建的 Actor, ActorSystem 是单例的(可以ActorSystem是一个工厂,专门创建Actor),一个 JVM 进程中有一个即可,而 Acotr 是可以有多个的。
3.6Actor模型是对并发模型进行了更高的抽象。(例如mailbox,dispatcherMessage是看不到的被封装起来了)
3.7Actor模型是异步、非阻塞、高性能的事件驱动编程模型。
3.8Actor模型是轻量级事件处理(1GB 内存可容纳百万级别个 Actor),因此处理大并发性能高。

  • 4.Actor模型工作机制和消息机制(如图)

并发编程模型AKKA_第2张图片
4.1Actor模型工作机制
4.1.1ActorySystem创建Actor。
4.1.2ActorRef:可以理解成是Actor的代理或者引用。消息是通过ActorRef来发送,而不能通过Actor 发送消息,通过哪个ActorRef 发消息,就表示把该消息发给哪个Actor。
4.1.3消息发送到Dispatcher Message (消息分发器),它得到消息后,会将消息进行分发到对应的MailBox。(注: Dispatcher Message 可以理解成是一个线程池, MailBox 可以理解成是消息队列,可以缓冲多个消息,遵守FIFO)。
4.1.4Actor 可以通过 receive方法来获取消息,然后进行处理。
4.2Actor间传递消息机制
并发编程模型AKKA_第3张图片
4.2.1每一个消息就是一个Message对象。Message 继承了Runable, 因为Message就是线程类。
4.2.2从Actor模型工作机制看上去很麻烦,但是程序员编程时只需要编写Actor就可以了,其它的交给Actor模型完成即可。
4.2.3A Actor要给B Actor 发送消息,那么A Actor 要先拿到(也称为持有) B Actor 的 代理对象ActorRef 才能发送消息 。

  • 5.应用 编写AKKA小demo

5.1环境准备
pom.xml中



    4.0.0

    hz.csq.akak
    AKKA
    1.0-SNAPSHOT

    
    
        UTF-8
        2.11.8
        2.11
        2.4.17
    

    
        
        
            org.scala-lang
            scala-library
            ${scala.version}
        

        
        
            com.typesafe.akka
            akka-actor_${scala.compat.version}
            ${akka.version}
        

        
        
            com.typesafe.akka
            akka-remote_${scala.compat.version}
            ${akka.version}
        
    

    
    
        
        src/main/scala
        src/test/scala
        
            
            
                net.alchim31.maven
                scala-maven-plugin
                3.2.2
                
                    
                        
                            compile
                            testCompile
                        
                        
                            
                                -dependencyfile
                                ${project.build.directory}/.scala_dependencies
                            
                        
                    
                
            

            
            
                org.apache.maven.plugins
                maven-shade-plugin
                2.4.3
                
                    
                        package
                        
                            shade
                        
                        
                            
                                
                                    *:*
                                    
                                        META-INF/*.SF
                                        META-INF/*.DSA
                                        META-INF/*.RSA
                                    
                                
                            
                            
                                
                                    reference.conf
                                
                                
                                
                                    com.itstaredu.spark.SparkWorker
                                
                            
                        
                    
                
            
        
    

5.2.1 需求1:单个actor通讯

package com.csq.akka.actor

import akka.actor.{Actor, ActorSystem, Props}
//1.当继承Actor后就是一个Actor,因此就要重写核心方法Receive
class HelloActor extends Actor{
    //1.receive方法会被Mailbox(实现了Runnable接口)调用
   //2.当该Actor的Mailbox方法接受到消息后就会调用receive
  //3.type Receive = scala.PartialFunction[scala.Any, scala.Unit]

  override def receive:Receive = {
    // 重写接受消息的偏函数,其功能是接受消息并处理
    case "靓仔吃啥!"=>println("阿姨我不饿,就想听你叫一声靓仔!")
    case "吃啥!" => println("阿姨,你还没叫我靓仔呢!")
    case _ =>{//因为MailBox是runnbale不能自己停,需要代码去停。
      println("接收到exit~指令,退出系统.....")
      context.stop(self) // 停止自己的actorRef
      context.system.terminate() // 关闭ActorSystem
    }



  }
}
object HelloActorDemo {
  //创建 ActorSystem
  private val actorFactory = ActorSystem("ActorFactory")
  //说明
  //1.通过  actorFactory 创建 SayHelloActor 对应的 ActorRef
  //2.目的是通过sayHelloActorRef 发送消息
  //3.Props[HelloActor] 表示创建的是 HelloActor 这个actor(通过反射机制)
  //4.actorFactory.actorOf 方法返回的是 actorRef,可以理解成是actor的代理对象/引用
  //4."Actor01" 是actor/actorref的名字,由程序员指定
  private val HelloActorRef = actorFactory.actorOf(Props[HelloActor], "Actor01")

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

    // 给HelloActorRef发送消息
    // !是方法名,可以看下源码
    HelloActorRef ! "靓仔吃啥!"
    HelloActorRef ! "吃啥!"

    //如果不发送exit, Actor就会等待接收消息,而不会退出程序.
    HelloActorRef ! "exit"

  }
}

结果
并发编程模型AKKA_第4张图片
小结
并发编程模型AKKA_第5张图片
a)当程序执行 aActorRef = actorFactory.actorOf(Props[AActor], “aActor”) ,会完成如下任务 [这是非常重要的方法]
b)actorFactory 是 ActorSystem(“ActorFactory”) 这样创建的。
这里的 Props[AActor] 会使用反射机制,创建一个AActor 对象,如果是c)actorFactory.actorOf(Props(new AActor(bActorRef)), “aActorRef”) 形式,就是使用new 的方式创建一个AActor对象, 注意Props() 是小括号。
d)会创建一个AActor 对象的代理对象 aActorRef , 使用aActorRef 才能发送消息
e)会在底层创建 Dispather Message ,是一个线程池,用于分发消息, 消息是发送到对应的Actor的 MailBox会在底层创建AActor 的MailBox 对象,该对象是一个队列,可接收Dispatcher Message 发送的消息
f)MailBox 实现了Runnable 接口,是一个线程,一直运行并调用Actor的receive 方法,因此当Dispather 发送消息到MailBox时,Actor 在receive 方法就可以得到信息.
g)aActorRef ! “吃啥!”, 表示把 "吃啥!"消息发送到A Actor 的mailbox (通过Dispatcher Message 转发)
5.2.2 Actor间的通讯
并发编程模型AKKA_第6张图片
需求:AActor和BActor的对话
AActor:

package com.csq.akka.actors

import akka.actor.{Actor, ActorRef}

class AActor(actorRef: ActorRef) extends Actor {

  val bActorRef: ActorRef = actorRef

  override def receive: Receive = {
    case "姐姐" => {
      println("AActor:姐姐这个菜真好吃!")
      bActorRef ! "AActor:姐姐这个菜真好吃!" //给BActor 发出消息
    }
    case "真可爱!那是我女儿和你差不多大,你该叫我啥!" => {
      //给BActor 发出消息 谁主动发消息就必须要有持有对方的引用
      //这里需要持有BActor的引用(BActorRef)这个不是自己的是传进来的,因此需要构建这个actor时就要传进来。
      println("AActor:妈妈耶!")
      Thread.sleep(1000)
      bActorRef ! "妈妈耶!" //给BActor 发出消息
    }

  }
}

BActor

package com.csq.akka.actors

import akka.actor.Actor

class BActor extends Actor{
  override def receive:Receive = {
    case "AActor:姐姐这个菜真好吃!" => {
      println("BActor:真可爱!那是我女儿和你差不多大,你该叫我啥!")
      Thread.sleep(1000)
      //通过sender() 可以获取到发现消息的actor的ref
      sender() ! "真可爱!那是我女儿和你差不多大,你该叫我啥!"
    }
    case "AActor:姐姐这个菜真好吃!" => {
      println("BActor:真可爱!那是我女儿和你差不多大,你该叫我啥!")
      Thread.sleep(1000)
      //通过sender() 可以获取到发现消息的actor的ref
      sender() ! "真可爱!那是我女儿和你差不多大,你该叫我啥!"
    }
    case "妈妈耶!" => {
      println("BActor:你脸皮真厚,还是叫姐姐吧!")
      Thread.sleep(1000)
      //通过sender() 可以获取到发现消息的actor的ref
      sender() ! "姐姐"
    }
  }
}

ActorGame

package com.csq.akka.actors

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

object ActorGame extends App {//不写main函数的话,可以继承App
  //创建ActorSystem
  val actorfactory = ActorSystem("actorfactory")
  //先创建BActor引用/代理
  val bActorRef: ActorRef = actorfactory.actorOf(Props[BActor], "bActor")
  //创建AActor的引用
  val aActorRef: ActorRef = actorfactory.actorOf(Props(new AActor(bActorRef)), "aActor")

  //aActor发起
  aActorRef ! "姐姐"
}


结果
并发编程模型AKKA_第7张图片
小结
a)两个Actor通讯机制和Actor 自身发消息机制基本一样,只是要注意如下
b)如果A Actor 在需要给B Actor 发消息,则需要持有B Actor 的 ActorRef,可以通过创建时,传入 B Actor的 代理对象(ActorRef)
c)当B Actor 在receive 方法中接收到消息,需要回复时,可以通过sender() 获取到发送Actor的 代理对象。

如何理解Actor 的receive 方法被调用?
a)每个Actor对应MailBox
b)MailBox 实现了Runnable 接口,处于运行的状态
c)当有消息到达MailBox,就会去调用Actor的receive方法,将消息推送给receive

你可能感兴趣的:(scala,spark)