Akka 简介
Akka 介绍
- Akka 是编写并发程序的框架,它提供了一种称为Actor的并发模型,其粒度比线程更小,你可以在系统中启用大量的Actor。
- Akka用Scala语言编写,同时提供了Scala和Java的开发接口。
- Akka的出现使得程序员不再过多的考虑线程、锁和资源竞争的问题,可以轻松的写出高效稳定的并发程序。
Akka 用于解决什么问题
单线程模型中,不会出现数据一致性的问题,但是系统的性能不能保证。当程序是多线程并发处理数据时需要保证数据的一致性和正确性,需要对代码加入同步条件synchronized,这样对程序效率就有很大的影响。
Akka是一种处理并发问题的解决方案,它的出现解决了这个问题,简化并发编程,提升程序性能。
Actor模型
Akka基于Actor模型,提供了一个用于构建可扩展的(Scalable)、弹性的(Resilient)、快速响应的(Responsive)应用程序的平台。
- 在基于Actor的系统里,所有的事物都是Actor,Actor于Actor之间只能通过消息通信,如图的信封。当Actor给另一个Actor发送消息时,消息是有顺序的,只需要将消息投递到相应的邮箱即可。
- 怎么处理消息是由接受消息的Actor决定,发送消息的Actor既可以等待回复,也可以异步处理。
- ActorSystem用来创建并管理Actor,ActorSystem可以理解为一个工厂,专门用来创建Actor,ActorSystem是单例的,一个JVM进程中只有一个即可,但是Actor可以有多个。
- Actor模型是轻量级的事件处理(1GB内存可以容纳百万个Actor),因此性能恒高。
Actor模型工作机制
- A actor和B actor需要继承Actor,并重写receive方法
- 通过ActorSystem创建actorfactory
- 通过actorfactory创建A ActorRef和B ActorRef
- ActorRef 可以理解成Actor的代理或者引用。消息通过ActorRef来发送,而不是通过Actor来发送,往哪个Actor发送消息需要先拿到对应的ActorRef才可以发送消息。例如上图A actor 向 B actor发送消息,那么A actor需要拿到B actor的ActorRef才可以向B actor发送消息。
- 发送消息到Dispatcher Message(消息分发器),它得到消息后会将消息发送到对应的MailBox中。其中MailBox可以理解成消息队列,能够缓冲多个消息,遵守FIFO原则。
- Actor 通过receive方法来获取消息,然后进行处理。
- 如图如果B actor接受到A actor的消息后回复A actor 需要通过sender()方法来获取到A actor的ActorRef,然后就可以回复A actor的消息。
- 每一个消息其实就是一个Message对象。Message继承了Runnable。
Actor应用实例
Maven依赖
<!-- 定义一下常量 -->
<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>
单Actor通信案例
单个 Actor 如何使用案例
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
//说明
//1. 当我们继承Actor后,就是一个Actor,核心方法receive 方法重写
class SayHelloActor extends Actor{
//说明
//1. receive方法,会被该Actor的MailBox(实现了Runnable接口)调用
//2. 当该Actor的MailBox 接收到消息,就会调用 receive
//3. type Receive = PartialFunction[Any, Unit]
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 SayHelloActorDemo {
//1. 先创建一个ActorSystem, 专门用于创建Actor
private val actoryFactory = ActorSystem("actoryFactory")
//2. 创建一个Actor的同时,返回Actor的ActorRef
// 说明
//(1) Props[SayHelloActor] 创建了一个 SayHelloActor实例,使用反射
//(2) "sayHelloActor" 给actor取名
//(3) sayHelloActorRef: ActorRef 就是 Props[SayHelloActor] 的ActorRef
//(4) 创建的SayHelloActor 实例被ActorSystme接管
private val sayHelloActorRef: ActorRef = actoryFactory.actorOf(Props[SayHelloActor],"sayHelloActor")
def main(args: Array[String]): Unit = {
//给SayHelloActor 发消息(邮箱)
sayHelloActorRef ! "hello"
sayHelloActorRef ! "ok"
sayHelloActorRef ! "ok~"
//研究异步如何退出ActorSystem
sayHelloActorRef ! "exit"
}
}
多actor通信案例
两个actor之间如何通信案例。具体通信过程参照注释中的序号。
- A actor
import akka.actor.{Actor, ActorRef}
class AActor(actorRef: ActorRef) extends Actor {
val bActorRef: ActorRef = actorRef
override def receive: Receive = {
//(1)从 start 开始,使用Scala的模式匹配
case "start" => {
println("AActor(黄飞鸿) 出招了 , 见招")
self ! "我打" //发给自己
}
//(2)
case "我打" => {
//给BActor 发出消息
//这里需要持有BActor的引用(BActorRef)
println("AActor(黄飞鸿) : 看我佛山无影脚")
Thread.sleep(1000)
bActorRef ! "我打" //给BActor 发出消息
}
//(4)
case "不要打我了" => {
//给BActor 发出消息
//这里需要持有BActor的引用(BActorRef)
println("AActor(黄飞鸿) :乔峰你太厉害了 放过我")
Thread.sleep(1000)
bActorRef ! "放过我" //给BActor 发出消息
}
}
}
- B actor
import akka.actor.Actor
class BActor extends Actor{
override def receive:Receive = {
//(3)
case "我打" => {
println("BActor(乔峰) :挺猛 看我降龙十八掌")
Thread.sleep(1000)
//通过sender() 可以获取到发送消息的actor的ref,也就是AActor的引用
sender() ! "不要打我了"
}
//(5)
case "放过我" => {
println("BActor(乔峰) :那就放过你吧")
Thread.sleep(1000)
context.stop(self) //停止BActor的ActorRef
//通过sender() 可以获取到发送消息的actor的ref,也就是AActor的引用
context.stop(sender())//停止AActor的ActorRef
context.system.terminate()//退出actorsystem
println("回合结束")
}
}
}
- 主函数
import akka.actor.{ActorRef, ActorSystem, Props}
object ActorGame extends App {
//创建ActorSystem
val actorfactory = ActorSystem("actorfactory")
//先创建BActor引用/代理
val bActorRef: ActorRef = actorfactory.actorOf(Props[BActor], "bActor")
//创建AActor的引用,并将bActorRef给到aActorRef --》(Props(new AActor(bActorRef)))
val aActorRef: ActorRef = actorfactory.actorOf(Props(new AActor(bActorRef)), "aActor")
//aActor出招 消息发送给A actor
aActorRef ! "start"
}
- 结果
深入了解Akka并发编程模型,请点击下面链接:
链接1: Akka网络编程案例.
链接2: Akka模拟Spark Master和Worker通信.