Scala并发编程模型 Akka

Akka 介绍

  1. Akka 是 JAVA 虚拟机 JVM 平台上构建高并发、分布式和容错应用的工具包和运行时,你可以理解成 Akka 是编写并发程序的框架。
  2. Akka 用 Scala 语言写成,同时提供了 Scala 和 JAVA 的开发接口。
  3. Akka 主要解决的问题是:可以轻松的写出高效稳定的并发程序,程序员不再过多的考虑线程、锁和资源竞争等细节。

Actor 模型用于解决什么问题

  1. 处理并发问题关键是要保证共享数据的一致性和正确性,因为程序是多线程时,多个线程对同一个数据进行修改,若不加同步条件,势必会造成数据污染。但是当我们对关键代码加入同步条件synchronized 后,实际上大并发就会阻塞在这段代码,对程序效率有很大影响。
  2. 若是用单线程处理,不会有数据一致性的问题,但是系统的性能又不能保证。
  3. Actor 模型的出现解决了这个问题,简化并发编程,提升程序性能。 你可以这里理解:Actor 模型是一种处理并发问题的解决方案,很牛!

Akka 中 Actor 模型

Actor 模型及其说明
  1. Akka 处理并发的方法基于 Actor 模型。(示意图)
  2. 在基于 Actor 的系统里,所有的事物都是 Actor,就好像在面向对象设计里面所有的事物都是对象一样。
  3. Actor 模型是作为一个并发模型设计和架构的。Actor 与 Actor 之间只能通过消息通信,如图的信封
  4. Actor 与 Actor 之间只能用消息进行通信,当一个 Actor 给另外一个 Actor 发消息,消息是有顺序的(消息队列),只需要将消息投寄的相应的邮箱即可。
  5. 怎么处理消息是由接收消息的 Actor 决定的,发送消息 Actor 可以等待回复,也可以异步处理
  6. ActorSystem 的职责是负责创建并管理其创建的 Actor, ActorSystem 是单例的(可以ActorSystem 是一个工厂,专门创建 Actor),一个 JVM 进程中有一个即可,而 Acotr 是可以有多个的。
  7. Actor 模型是对并发模型进行了更高的抽象。
  8. Actor 模型是 异步、 、 非阻塞、 、 高性能的事件驱动编程模型。[案例: 说明 什么是异步、非阻塞, 最经典的案例就是 ajax 异步请求处理 ]
  9. Actor 模型是轻量级事件处理(1GB 内存可容纳百万级别个 Actor),因此处理大并发性能高.
Actor 模型工作机制说明

在这里插入图片描述

Actor 模型的工作机制

1.先创建ActorSystem
2.通过ActorSystem创建对应的Actor、ActorRef。 ActorRef:可以理解成是Actor的代理或者引用。消息是通过ActorRef来发送,而不能通过Actor 发送消息。通过哪个 ActorRef 发消息,就表示把该消息发给ActorRef 对应的 Actor
3.通过ActorRef ! 消息 (表示将消息发送到ActorRef对应的Actor的MailBox)
4.先将消息发送给Dispatcher Message。(注: Dispatcher Message 可以理解成是一个线程池, MailBox 可以理解成是消息队列,可以缓冲多个消息,遵守 FIFO)
5.由Dispatcher Message将消息转发对应的Actor的MailBox
6.当Actor的MailBox收到消息后,就会调用Actor的Receive方法,把消息推送给Actor
7.如果希望回复消息,可通过 sender() ! ”消息”

Actor 模型的消息机制
  1. 每一个消息就是一个 Message 对象。Message 继承了 Runable, 因为 Message 就是线程类。
  2. 从 Actor 模型工作机制看上去很麻烦,但是程序员编程时只需要编写 Actor 就可以了,其它的交给 Actor 模型完成即可。
  3. AActor 要给 B Actor 发送消息,那么 A Actor 要先拿到(也称为持有) B Actor 的 代理对象ActorRef 才能发送消息。

示例1

需求:

  1. 编写一个 Actor, 比如 SayHelloActor
  2. SayHelloActor 可以给自己发送消息 ,如图
    在这里插入图片描述
    对应的pom文件需要添加的依赖
<!-- 定义一下常量 -->
    <properties>
        <encoding>UTF-8</encoding>
        <scala.version>2.11.8</scala.version>
        <scala.compat.version>2.11</scala.compat.version>
        <akka.version>2.4.17</akka.version>
    </properties>

    <dependencies>
        <!-- 添加scala的依赖 -->
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>

        <!-- 添加akka的actor依赖 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_${scala.compat.version}</artifactId>
            <version>${akka.version}</version>
        </dependency>

        <!-- 多进程之间的Actor通信 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-remote_${scala.compat.version}</artifactId>
            <version>${akka.version}</version>
        </dependency>
    </dependencies>

    <!-- 指定插件-->
    <build>
        <!-- 指定源码包和测试包的位置 -->
        <sourceDirectory>src/main/scala</sourceDirectory>
        <testSourceDirectory>src/test/scala</testSourceDirectory>
        <plugins>
            <!-- 指定编译scala的插件 -->
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                        <configuration>
                            <args>
                                <arg>-dependencyfile</arg>
                                <arg>${project.build.directory}/.scala_dependencies</arg>
                            </args>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- maven打包的插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.4.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>reference.conf</resource>
                                </transformer>
                                <!-- 指定main方法 -->
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>xxx</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

class SayHelloActor


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

//当继承Actor后,就是一个Actor,核心方法receive方法重写
class SayHelloActor extends Actor{
  override def receive: Receive = {
    case "hello" => println("收到hello,回应hello too :)")
    case "ok" =>println("收到ok,回应 ok too :)")
    case "exit" => {
      println("接收到exit指令,退出系统")
      context.stop(self) //停止actoref
      context.system.terminate() //退出 actorsystem
    }
    case _ =>println("匹配不到 :(")
  }
}


object SayHelloActorTest{
  //创建一个ActorSystem,专门用于创建Actor
  private val actorFactory = ActorSystem("actorFactory")
  //创建一个Actor的同时,返回Actor的ActorRef
  //详细说明
  //Props[SayHelloActor],使用反射创建了一个SayHelloActor实例
  //"SayHelloActor"给actor取名
  //sayHelloActorRef:ActorRef 就是 Props[SayHelloActor] 的 ActorRef
  //创建的 SayHelloActor 实例被 ActorSystem 接管
  private val sayHelloActorRef: ActorRef = actorFactory.actorOf(Props[SayHelloActor],"sayHelloActor")

  def main(args: Array[String]): Unit = {
    println("start~")
    //给 SayHelloActor 发消息(邮箱)
    sayHelloActorRef ! "hello"
    sayHelloActorRef ! "ok"
    sayHelloActorRef ! "mmmmmm"
    sayHelloActorRef ! "exit"
  }

}

在这里插入图片描述

示例2: Actor 间通讯

需求

  1. 编写 2 个 Actor , 分别是 AActor 和 BActor
  2. A_Actor 和 B_Actor 之间可以相互发送消息.

class A_Actor


import akka.actor.{Actor, ActorRef}

//A_Actor要想向B_Actor发送消息,必须持有B_Actor的b_actorRef
class A_Actor (actorRef:ActorRef) extends Actor {
  val b_actorRef = actorRef
  var count=0
  override def receive: Receive = {
    case "start" => {
      println("相遇开始")
      self ! "A"
    }
    case "A" =>{
      count = count + 1
      if(count==5){

        context.stop(b_actorRef)
        context.system.terminate()
        println("打招呼5次了,歇会儿")
      }
      println("A_Actor:你好,我是A!你呢?")
      Thread.sleep(1000)
      b_actorRef ! "B"

    }      
  }
}

class B_Actor

class B_Actor extends Actor{
  override def receive: Receive = {
    case "B" => {
      println("B_Actor:你好,我是B,你呢?")
      sender() !  "A"
    }
  }
}

object ActorGame


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

object ActorGame {
  //创建一个ActorSystem,专门用于创建Actor
  val actorFactory = ActorSystem("actorFactory")
  //创建的 SayHelloActor 实例被 ActorSystem 接管
  val b_ActorRef:ActorRef = actorFactory.actorOf(Props[B_Actor],"B_Actor")
  //因为创建A_Actor需要传入b_ActorRef,因此在创建A_Actor的时候,要用new 对象的形式
  val a_ActorRef: ActorRef = actorFactory.actorOf(Props(new A_Actor(b_ActorRef)),"A_Actor")

  def main(args: Array[String]): Unit = {
    a_ActorRef ! "start"
  }
}

在这里插入图片描述

示例3:客户端服务器间通信

在这里插入图片描述
客户端


import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import com.xyq.yellowchicken.commen.{ClientMessage, ServerMessage}

import scala.io.StdIn

//小黄鸡客户端的Actor
class CustomerActor(serverHost:String,serverPort:Int) extends Actor{
  //定义一个 YellowChickenServerRef
  var serverActorRef: ActorSelection = _

  //在 Actor 中有一个方法 PreStart 方法,他会在 actor 运行前执行
  //在 akka 的开发中,通常将初始化的工作,放在 preStart 方法

  override def preStart(): Unit = {
    println("preStart() 执行")
    serverActorRef = context.actorSelection(s"akka.tcp://Server@${serverHost}:${serverPort}/user/YellowChickenServer")
    println("serverActorRef=" + serverActorRef)
  }

  override def receive: Receive = {
    case "start" => {
      println("上线了")
    }

    case mes:String =>{
      serverActorRef ! ClientMessage(mes)
    }
    case ServerMessage(mes) => {
      println(mes)
    }
  }
}

object YellowChickenClient extends App{
  val (clientHost, clientPort, serverHost, serverPort) = ("127.0.0.1", 9990, "127.0.0.1", 9999)
  val config = ConfigFactory.parseString(
    s"""
       |akka.actor.provider="akka.remote.RemoteActorRefProvider"
       |akka.remote.netty.tcp.hostname=$clientHost
       ||akka.remote.netty.tcp.port=$clientPort
       |""".stripMargin)

  //创建 ActorSystem
  val clientActorSystem =ActorSystem("client", config)
  //创建 CustomerActor 的实例和引用
  val customerActorRef: ActorRef = clientActorSystem.actorOf(Props(new CustomerActor(serverHost,
    serverPort)), "CustomerActor")
  //启动 customerRef/也可以理解启动 Actor
  customerActorRef ! "start"
  //客户端可以发送消息给服务器
  println("请输入要咨询的问题")
  while (true) {
    val mes = StdIn.readLine()
    customerActorRef ! mes
  }
}

服务端


import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.BackoffSupervisor
import com.typesafe.config.ConfigFactory
import com.xyq.yellowchicken.commen.{ClientMessage, ServerMessage}

//小黄鸡服务端的Actor
class YellowChickenServer extends Actor{
  override def receive: Receive = {
    case ClientMessage(mes) =>{
      mes match {
        case "在吗?" => sender() ! ServerMessage("在的,亲!")
        case "你的名字?" => sender() ! ServerMessage("小黄鸡")
        case _ => sender() !  ServerMessage("没听清,请再说一遍")
      }
    }
  }
}

object YellowChickenServer extends App{
  val host = "127.0.0.1" //服务端 ip 地址
  val port = 9999
  //创建 config 对象,指定协议类型,监听的 ip 和端口
  val config = ConfigFactory.parseString(
    s"""
       |akka.actor.provider="akka.remote.RemoteActorRefProvider"
       |akka.remote.netty.tcp.hostname=$host
       |akka.remote.netty.tcp.port=$port
""".stripMargin)

  private val serverActorSystem: ActorSystem = ActorSystem("Server",config)
  private val yellowChickenServerRef: ActorRef = serverActorSystem.actorOf(Props[YellowChickenServer],"YellowChickenServer")
}

样例类

case class ClientMessage(mes:String)
case class ServerMessage(mes:String)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值