Akka-Actor之Hello-World

99封情书 提交于 2020-03-18 18:34:55

3 月,跳不动了?>>>

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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!