Akka简介
Akka是一个工具包和运行时(Runtime),用于构建高度并发,分布式和容错的事件驱动应用程序。一般用于Java构建基于Actor的系统。
实质上:Akka - Akka是一个基于actor模型构建反应式应用程序的库
这里解释几个名词:单片架构又称企业存储阵列阵列包含一个共享的记忆体架构以及多个冗余组件。
关键词:Akka,Gradle,Maven。
首先构建一个入门的demo
在进行官网的指示进行环境的配置之后
要求是
- jdk 1.8以及以上
- Maven支持
我们在项目目录的文件夹下运行
mvn compile exec:exec
可以在一系列的download build之后可以跑出如下的结果
首先我们弄明白这个程序在runtime干了什么
-
main class 创建了一个 akka.actor.ActorSystem, 一个Actor模型可以行驶其功能的container .
-
创建了三个GreetActor实例
- Howdy
- Hello
- Good day
-
创建了一个printActor实例
-
然后就是实例间消息的传递
用口语化的描述就是主类现在给三个greeter类型的Actor安排任务
就是那个叫howdy的,我告诉你和谁发消息,消息的内容是啥
那个谁来着,对叫hello的,我也告诉你下
那个叫goodDay的,我再和你说下
好了三个小伙伴就带着任务去找printer了,printer哥哥帮我干下活吧,嗯就是这样的一个过程
那么这样干的好处在哪呢
Akka’s use of Actors and asynchronous messaging result in a range of benefits
AKKA的以下特性允许您以直观的方式解决困难的并发性和可扩展性挑战:
- 事件驱动的模型
Actors响应消息执行工作。Actors之间的通信是异步的,允许Actor发送消息并继续自己的工作而不阻塞等待答复。
- 强隔离原则
与Java中的常规对象不同,在API方面,没有一个公共API。相反,它的公共API通过Actor处理的消息来定义。这阻止了参与者之间的状态共享;观察另一个演员状态的唯一方法是发送一个请求它的消息。
-
位置透明
系统从工厂构建Actor并返回对实例的引用。因为位置不重要,演员实例可以启动、停止、移动和重新启动,以放大和缩小,以及从意外故障中恢复(因为我在哪儿都没关系,不存在牵一发而动全身,我是自由的)。
-
轻量级
每个实例只消耗几百个字节,这于现实意义上允许数百万个并发Actor存在于单个应用程序中。
Actor 模型的构建
定义Actor和消息
消息可以是任意类型(任何子类型Object)。可以发送原始值(如String,Integer,Boolean作为消息等)以及纯数据结构如数组和集合类型。
Hello World Actors使用了三种不同的消息:
WhoToGreet:问候的收件人
Greet:执行问候语的指令
Greeting:包含问候语的消息
在定义Actors及其消息时,请记住以下建议:
由于消息是Actor的公共API,因此定义具有良好名称和丰富语义和域特定含义的消息是一种很好的做法,即使它们只是包装您的数据类型。这样可以更容易地使用,理解和调试基于actor的系统。
消息应该是 不可变 的,因为它们在不同的线程之间共享。
将actor的关联消息作为静态类放在Actor的类中是一种很好的做法。这使得更容易理解actor期望和处理的消息类型。
在Actor的类中使用静态方法props描述如何构造Actor 也是一种常见的模式。
访问和修改 一个Actor的内部状态是线程安全的,因为这一点被Actor模型所维护
让我们看看Actor如何实现Greeter并Printer演示这些最佳实践。
The Greeter Actor
/**
*Greeter继承了 akka.actor.AbstractActor class 实现了createReceive接口.
*
**/
package com.lightbend.akka.sample;import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;
import com.lightbend.akka.sample.Printer.Greeting;public class Greeter extends AbstractActor {static public Props props(String message, ActorRef printerActor) {return Props.create(Greeter.class, () -> new Greeter(message, printerActor));}static public class WhoToGreet {public final String who;public WhoToGreet(String who) {this.who = who;}}static public class Greet {public Greet() {}}private final String message;private final ActorRef printerActor;//The greeting variable contains the Actor’s stateprivate String greeting = "";//The Greeter构造方法接收两个参数: String message和ActorRef printerActor可理解为消息传递后参考输出的模板.public Greeter(String message, ActorRef printerActor) {this.message = message;this.printerActor = printerActor;}//The receiveBuilder定义了Actor应该如何应对他接收到的消息.
//在这个例子中这个receive希望得到两种类型的message: WhoToGreet and Greet.前者 update the greeting state of the Actor 后者触发the Printer Actor发送greeting.@Overridepublic Receive createReceive() {return receiveBuilder().match(WhoToGreet.class, wtg -> {this.greeting = message + ", " + wtg.who;}).match(Greet.class, x -> {printerActor.tell(new Greeting(greeting), getSelf());}).build();}
}
Printer Actor
通过
Logging.getLogger(getContext().getSystem(), this);
创建一个记录器.借此我们可以在Actor中调用log.info() 写出信息.仅仅处理一种类型的消息, Greeting然后记录其包含的内容.
package com.lightbend.akka.sample;import akka.actor.AbstractActor;
import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;public class Printer extends AbstractActor {static public Props props() {return Props.create(Printer.class, () -> new Printer());}static public class Greeting {public final String message;public Greeting(String message) {this.message = message;}}private LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);public Printer() {}@Overridepublic Receive createReceive() {return receiveBuilder().match(Greeting.class, greeting -> {log.info(greeting.message);}).build();}
}
位置透明度的力量
在Akka,位置无所谓。位置透明性意味着ActorRef可以在保留相同语义的同时,表示正在运行的actor在进程中或远程计算机上的实例。如果需要,运行时可以通过在运行时更改Actor的位置或整个应用程序拓扑来优化系统。这使得故障管理的"let it crash"模型成为可能,系统可以通过崩溃故障的Actors并重新启动健康的Actors来自我修复。
Actor 创建
在Akka中,您无法使用new关键字创建Actor的实例。相反,您使用工厂创建Actor实例。工厂不返回actor实例,而是返回指向actor实例的引用akka.actor.ActorRef。这种间接级别在分布式系统中增加了很多功能和灵活性。
The akka.actor.ActorSystem factory表现的更像一个容器,他来维护Actor,以及维护其生命周期
来看一下如何创建
final ActorRef printerActor =system.actorOf(Printer.props(), "printerActor");
final ActorRef howdyGreeter =system.actorOf(Greeter.props("Howdy", printerActor), "howdyGreeter");
final ActorRef helloGreeter =system.actorOf(Greeter.props("Hello", printerActor), "helloGreeter");
final ActorRef goodDayGreeter =system.actorOf(Greeter.props("Good day", printerActor), "goodDayGreeter");
异步通信
the sender puts the message in the recipient’s mailbox and is free to do other work.
这句话说的是Actor异步通信的要义在于发完消息之后的Actor就处于自由状态(暂停的状态不会花费额外的资源)了,不会浪费时间在等接受方的ACK,
而这个mailbox实质上是一个消息队列:保留从同一个Actor发送的多个消息的顺序,但可以与另一个Actor发送的消息交错。
要将消息放入Actor的邮箱,请在ActorRef上使用tell方法。例如,Hello World的主类将消息发送到Greeter Actor,如下所示:
注意向谁发送邮件就调用该对象的tell方法
howdyGreeter.tell(new WhoToGreet("Akka"), ActorRef.noSender());
howdyGreeter.tell(new Greet(), ActorRef.noSender());howdyGreeter.tell(new WhoToGreet("Lightbend"), ActorRef.noSender());
howdyGreeter.tell(new Greet(), ActorRef.noSender());helloGreeter.tell(new WhoToGreet("Java"), ActorRef.noSender());
helloGreeter.tell(new Greet(), ActorRef.noSender());goodDayGreeter.tell(new WhoToGreet("Play"), ActorRef.noSender());
goodDayGreeter.tell(new Greet(), ActorRef.noSender());
然后就是Greeter Actor向 Printer Actor发送消息:
printerActor.tell(new Greeting(greeting), getSelf());