Akka 介绍
- Akka 是 JAVA 虚拟机 JVM 平台上构建高并发、分布式和容错应用的工具包和运行时,你可以理解成 Akka 是编写并发程序的框架。
- Akka 用 Scala 语言写成,同时提供了 Scala 和 JAVA 的开发接口。
- Akka 主要解决的问题是:可以轻松的写出高效稳定的并发程序,程序员不再过多的考虑线程、锁和资源竞争等细节。
Actor 模型用于解决什么问题
- 处理并发问题关键是要保证共享数据的一致性和正确性,因为程序是多线程时,多个线程对同一个数据进行修改,若不加同步条件,势必会造成数据污染。但是当我们对关键代码加入同步条件synchronized 后,实际上大并发就会阻塞在这段代码,对程序效率有很大影响。
- 若是用单线程处理,不会有数据一致性的问题,但是系统的性能又不能保证。
- Actor 模型的出现解决了这个问题,简化并发编程,提升程序性能。 你可以这里理解:Actor 模型是一种处理并发问题的解决方案,很牛!
Akka 中 Actor 模型
Actor 模型及其说明
- Akka 处理并发的方法基于 Actor 模型。(示意图)
- 在基于 Actor 的系统里,所有的事物都是 Actor,就好像在面向对象设计里面所有的事物都是对象一样。
- Actor 模型是作为一个并发模型设计和架构的。Actor 与 Actor 之间只能通过消息通信,如图的信封
- Actor 与 Actor 之间只能用消息进行通信,当一个 Actor 给另外一个 Actor 发消息,消息是有顺序的(消息队列),只需要将消息投寄的相应的邮箱即可。
- 怎么处理消息是由接收消息的 Actor 决定的,发送消息 Actor 可以等待回复,也可以异步处理
- ActorSystem 的职责是负责创建并管理其创建的 Actor, ActorSystem 是单例的(可以ActorSystem 是一个工厂,专门创建 Actor),一个 JVM 进程中有一个即可,而 Acotr 是可以有多个的。
- Actor 模型是对并发模型进行了更高的抽象。
- Actor 模型是 异步、 、 非阻塞、 、 高性能的事件驱动编程模型。[案例: 说明 什么是异步、非阻塞, 最经典的案例就是 ajax 异步请求处理 ]
- 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 模型的消息机制
- 每一个消息就是一个 Message 对象。Message 继承了 Runable, 因为 Message 就是线程类。
- 从 Actor 模型工作机制看上去很麻烦,但是程序员编程时只需要编写 Actor 就可以了,其它的交给 Actor 模型完成即可。
- AActor 要给 B Actor 发送消息,那么 A Actor 要先拿到(也称为持有) B Actor 的 代理对象ActorRef 才能发送消息。
示例1
需求:
- 编写一个 Actor, 比如 SayHelloActor
- 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 间通讯
需求
- 编写 2 个 Actor , 分别是 AActor 和 BActor
- 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)