一、什么是Akka?
Akka是一个异步非阻塞高并发处理框架,它是一系列框架,包括akka-actor, akka-remote, akka-cluster, akka-stream等,分别具有高并发处理模型——actor模型,远程通信,集群管理,流处理等功能。Akka具有一下特点:
Akka
屏蔽了Java的多线程和锁,转而使用Actor
模型,一般的工程师可以在不了解如何优化Java多线程编程的情况下,也能实现非常高性能的系统Actor
设计之初就天然满足了分布式,而且粒度较小,单机上可以跑上百万的Actor
Akka
屏蔽了分布式集群中底层的通讯机制,对于开发者来说,只要根据业务写好Actor
即可Akka
直接提供了分布式下高可用、弹性、动态扩容的功能,无需再次开发。
二、Actor模型
由于AKka
的核心是Actor
,而Actor
是按照Actor模型
进行实现的,所以在使用Akka
之前,有必要弄清楚什么是Actor模型
。Actor模型
最早是1973年Carl Hewitt、Peter Bishop和Richard Seiger的论文中出现的,受物理学中的广义相对论(general relativity)和量子力学(quantum mechanics)所启发,为解决并发计算的一个数学模型。
Actor模型
所推崇的哲学是”一切皆是Actor“,这与面向对象编程的”一切皆是对象“类似。但不同的是,在模型中,Actor
是一个运算实体,它遵循以下规则:
- 接受外部消息,不占用调用方(消息发送者)的CPU时间片
- 通过消息改变自身的状态
- 创建有限数量的新
Actor
- 发送有限数量的消息给其他
Actor
很多语言都实现了Actor模型
,而其中最出名的实现要属Erlang
的。Akka
的实现借鉴了不少Erlang
的经验。
三、Actor模型的实现
Akka
中Actor
接受外部消息是靠Mailbox
,参见下图
对于Akka
,它又做了一些约束:
- 消息是不可变的
- Actor本身是无状态的
基本的Actor例子
引入依赖
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.4.7</version>
</dependency>
编写Actor
public class ActorDemo extends UntypedActor {
private LoggingAdapter log = Logging.getLogger(this.getContext().system(), this);
@Override
public void onReceive(Object message) throws Exception, Exception {
if (message instanceof String) {
log.info(message.toString());
System.out.println(getSelf() + "---->" + message + " " + Thread.currentThread().getName());
} else {
unhandled(message);
}
}
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("sys");
ActorRef actorRef = system.actorOf(Props.create(ActorDemo.class), "actorDemo");
actorRef.tell("Hello Akka", ActorRef.noSender());
}
}
注意:ActorSystem
是一个较重的存在,一般一个应用里,只需要一个ActorSystem
。
在同一个ActorySystem
中,Actor不能重名。
Actor的Path
Akka
中的Actor
不能直接被new出来,而是按一棵树来管理的,每个Actor
都有一个树上的path
:
实际上,在我们创建自己的Actor
之前,Akka
已经在系统中创建了三个名字中带有guardian
的Actor
:
/
最顶层的root guardian
。它是系统中所有Actor
的父,系统停止时,它是最后一个停止的/user
guardian
。这是用户自行创建的所有Actor
的父。这里的user跟用户没有一毛钱关系/system
系统guardian
在上面的例子里,我们使用的是system.actorOf
来创建Actor
,actorOf
返回的并不是Actor
自身,而是一个ActorRef
,它屏蔽了Actor
的具体物理地址(可能是本jvm,也可以是其他jvm或另一台机器)。通过直接打印ActorRef
看到Actor
的path
,比如本例是/app/user/echoActor
。
像这种直接由system创建出来的Actor
被称为顶层Actor
,一般系统设计的时候,顶层Actor
数量往往不会太多,大都由顶层Actor
通过getContext().actorOf()
派生出来其他的Actor
。
Actor间的相互调用(tell, ask)
在Actor模型
中,Actor
本身的执行是不占用被调用方(akka
中的话是消息的发送者)的CPU时间片,所以,akka
的Actor
在相互调用时均是异步的行为。
- tell 发送一个消息到目标
Actor
后立刻返回 - ask 发送一个消息到目标
Actor
,并返回一个Future
对象,可以通过该对象获取结果。但前提是目标Actor
会有Reply才行,如果没有Reply,则抛出超时异
Tell: Fire-forget
target.tell(message, getSelf());
其中第二个参数是发送者。之所以要带上这个是为了方便target处理完逻辑后,如果需要返回结果,可以也通过tell
异步通知回去。
Ask: Send-And-Recieve-Future
Future<Object> future = Patterns.ask(echoActor, "echo me", 200);
future.onSuccess(new OnSuccess<Object>() {
@Override
public void onSuccess(Object result) throws Throwable {
System.out.println(result);
}
}, system.dispatcher());