Akka-Actor之Hello-World
actor是一种并发模型,akka是使用scala语言实现这种并发模型的一个库。在akka中,消息在不同的actor之间传递,以此来驱动任务的执行,这和一般的方法调用的方式有明显的区别。
每个actor都有自己的“地址”,这个地址可以用来唯一的标示一个actor实例,每个actor都有一个个“邮箱”,希望与该actor通信的其他actor将消息根据其“地址”发送其邮箱,邮箱可以看作是一个消息队列,actor会从其“邮箱”取出消息,根据不同的消息值做不同的逻辑处理。
下面通过例子代码来学习Actor,在这之前我们需要新建一个maven项目,并引入akka依赖:
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.13</artifactId>
<version>2.6.4</version>
</dependency>
注意,最开始akka的actor是untyped的,后来较新版的akka出了typed的,上面的依赖是untyped的,这里学习的也是untyped的。对于typed的,依赖是:
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor-typed_2.13</artifactId>
<version>2.6.4</version>
</dependency>
actor
定义一个actor
在演示actor之前我们先定义一个简单的actor
既然actor的核心逻辑就是处理其他actor发过来的消息,那么actor应该有一个方法就是用于处理消息的。
我们通过继承UntypedAbstractActor
来实现自己的actor,通过Override名为onReceive
的方法来处理消息。
DemoActor.java
import akka.actor.UntypedAbstractActor;
public class DemoActor extends UntypedAbstractActor {
@Override
public void onReceive(Object message) throws Throwable {
System.out.println("Received message : " + message.toString());
}
}
上面的DemoActor
在接收到消息后打印了消息。
实例化actor
actor并不支持像普通的Java类一样,通过new操作来新建一个实例。下面的代码虽然可以编译通过,但是执行时会报错:
Main.java
public class Main {
public static void main(String[] args) {
DemoActor demoActor = new DemoActor();
}
}
那么怎么实例化一个actor实例呢?看下面的代码:
Main.java
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class Main {
public static void main(String[] args) {
ActorSystem actorSystem = ActorSystem.create("test");
ActorRef actorRef = actorSystem.actorOf(Props.create(DemoActor.class), "demo");
}
}
我们不能直接得到Actor实例,只能得到ActorRef,它是一个Actor实例的引用,对Actor发消息也只能通过调用ActorRef的方法来实现。实际上,akka的ActorSystem可以是分布式的,假设有2台机器A和B,一个真正的Actor实例可以是跑在B机器上的,而在A机器上可以得到ActorRef,它是对B机器上的Actor的引用,我们对ActorRef的操作实际是操作的B机器的Actor,ActorRef为我们屏蔽了底层的网络传输。
ActorSystem
什么是ActorSystem呢?可以把ActorSystem比喻成线程池,而Actor是线程池里面的线程。线程池里面的线程当然是线程池来负责创建,而不是显示手动创建。要得到ActorRef,我们先要得到ActorSystem。
ActorSystem是一个很重量级的对象,一般一个JVM里面应该只创建一个。创建ActorSystem时需要指定一个名字。如上面的代码所示。
ActorSystem把里面的actor组织成树状的组织结构,就像linux的文件系统一样,linux的path(目录和文件)就是树状结构,除了根path“/”以外,每个path都有唯一的父path。ActorSystem里面每个Actor都有其ActorPath
,一个
那么我们上面的代码
ActorSystem actorSystem = ActorSystem.create("untyped-actor-system");
创建的这个“system”里根路径是啥呢?通过lookupRoot方法可以得到“根actor“:
public static void main(String[] args) {
ActorSystem actorSystem = ActorSystem.create("test");
ActorRef actorRef = actorSystem.lookupRoot();
ActorPath path = actorRef.path();
System.out.println(path);
actorSystem.terminate();
}
输出:
akka://test/
可以看出“根actor“使用像文件系统的表示方式,可以表示为:akka://${name}/
使用ActorSystem的actorOf方法新添加一个actor时需要指定一个actor的名字,这个名字需要是唯一的,同一个ActorSystem下,显然不能有2个actor是一样的名字。那么新添加的actor其路径表示是啥呢?
public static void main(String[] args) {
ActorSystem actorSystem = ActorSystem.create("test");
ActorRef actorRef = actorSystem.actorOf(Props.create(DemoActor.class), "demo");
ActorPath path = actorRef.path();
System.out.println(path);
actorSystem.terminate();
}
输出:
akka://test/user/demo
可以看出路径是akka://test/user/demo
,而不是akka://test/demo
,中间多了一层”user“
实际上create一个ActorSystem后,在我们使用actorOf添加actor之前,ActorSystem里并不是出了根以以外一个actor都没有,至少有3个:
akka://test/system
是“系统actor”,它还会有一些已经存在的子acotr,我们创建的acotr都是akka://test/user
的子actor,如果在上面的demoActor中我们又创建了一个actor,名为“foo”,那么它的父亲akka://test/user/demo
,而其自己的路径是akka://test/user/demo/foo
发送消息
发送消息使用ActorRef的tell方法和ask方法,tell方法不希望对方回复,而ask方法希望对方回复。
下面演示使用tell方法发送字符串“hello”给demoActor,demoActor应该会打印出它接收到的消息。
tell方法的签名:
public final void tell(final Object msg, final ActorRef sender)
第一个参数就是要发送的消息,第二个参数也会ActorRef,表示发送者actor,前文有说过,消息在不同的actor之间传递,也就是说一个acotr接收到的消息肯定是另一个actor发给它的。那我们需要再写一个发送者actor吗?这里不需要,tell方法不需要对方回复,我们可以使用一个“匿名”的actor发送,这个“匿名”的acotr由ActorRef.noSender()
得到,其实它就是前面说的akka://test/deadLetters
public static void main(String[] args) {
ActorSystem actorSystem = ActorSystem.create("test");
ActorRef actorRef = actorSystem.actorOf(Props.create(DemoActor.class), "demo");
actorRef.tell("hello", ActorRef.noSender());
actorSystem.terminate();
}
输出:
Received message : hello
来源:oschina
链接:https://my.oschina.net/u/1393056/blog/3197598