Actors介绍-简单轮训实例

akka版本2.6.9
版权声明:本文为博主原创文章,未经博主允许不得转载。

 在使用Akka Actors时需要导入相关依赖(默认介绍maven的,如果您是使用sbt或gradle自行官网查看)

<properties>
  <akka.version>2.6.10</akka.version>
  <scala.binary.version>2.13</scala.binary.version>
</properties>
<dependency>
  <groupId>com.typesafe.akka</groupId>
  <artifactId>akka-actor-typed_${scala.binary.version}</artifactId>
  <version>${akka.version}</version>
</dependency>

 Akka模块的Java和Scala dsl都捆绑在同一个JAR中。为了获得平稳的开发体验,在使用Eclipse或IntelliJ这样的IDE时,您可以禁用自动导入器,使其在使用Scala时不建议javadsl导入,或者相反。

Akka Actors
 Actor模型为编写并发和分布式系统提供了更高层次的抽象。它减轻了开发人员必须处理显式锁和线程管理的负担,使编写正确的并发和并行系统变得更加容易。actor是Carl Hewitt在1973年的论文中定义的,但由于Erlang语言的使用而得到推广,例如在Ericsson中就成功地用于构建高度并发和可靠的电信系统。Akka的actor的API借用了Erlang的一些语法。

 如果你是Akka的新手,你可能想先阅读入门指南,然后再来这里学习更多。我们也推荐观看Akka Actors的简短介绍视频。

 熟悉Actor的基本的、外部的和内部的生态系统是很有帮助的,看看您可以根据需要利用和定制什么,查看参与者系统和参与者引用、路径和地址。
 正如在Actor系统中所讨论的,Actor是关于在独立的计算单元之间发送消息的,但是这看起来像什么呢?
 在以下所有这些导入都是依赖的:

import akka.actor.typed.ActorRef;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.AbstractBehavior;
import akka.actor.typed.javadsl.ActorContext;
import akka.actor.typed.javadsl.Behaviors;
import akka.actor.typed.javadsl.Receive;

 有了这些,我们就可以定义第一个Actor,它会说hello!
在这里插入图片描述

public class HelloWorld extends AbstractBehavior<HelloWorld.Greet> {

  public static final class Greet {
    public final String whom;
    public final ActorRef<Greeted> replyTo;

    public Greet(String whom, ActorRef<Greeted> replyTo) {
      this.whom = whom;
      this.replyTo = replyTo;
    }
  }

  public static final class Greeted {
    public final String whom;
    public final ActorRef<Greet> from;

    public Greeted(String whom, ActorRef<Greet> from) {
      this.whom = whom;
      this.from = from;
    }
  }

  public static Behavior<Greet> create() {
    return Behaviors.setup(HelloWorld::new);
  }

  private HelloWorld(ActorContext<Greet> context) {
    super(context);
  }

  @Override
  public Receive<Greet> createReceive() {
    return newReceiveBuilder().onMessage(Greet.class, this::onGreet).build();
  }

  private Behavior<Greet> onGreet(Greet command) {
    getContext().getLog().info("Hello {}!", command.whom);
    command.replyTo.tell(new Greeted(command.whom, getContext().getSelf()));
    return this;
  }
}

 这一小段代码定义了两种消息类型,一种用于命令Actor向某人打招呼,另一种用于Actor确认它已经这样做了。Greet类型不仅包含要打招呼的对象的信息,它还包含消息发送方提供的ActorRef,以便HelloWorld Actor可以发送回确认消息。
 在接收行为工厂的帮助下,Actor的行为被定义为迎宾者。处理下一条消息会导致可能与此消息不同的新行为。状态通过返回一个持有新的不可变状态的新行为来更新。在这种情况下,我们不需要更新任何状态,所以我们返回this,这意味着下一个行为“与当前行为相同”。
 此行为处理的消息类型被声明为类Greet。通常,参与者处理不止一种特定的消息类型,其中所有的消息类型都直接或间接实现一个公共接口。
 在最后一行,我们看到HelloWorld参与者向另一个参与者发送消息,这是使用tell方法完成的。它是一个不阻塞调用者线程的异步操作。
 由于replyTo地址被声明为ActorRef<Greeted>类型,编译器将只允许我们发送这种类型的消息,其他使用将是编译器错误。
 Actor接受的消息类型与所有应答类型一起定义了该Actor所说的协议;在本例中,它是一个简单的请求-应答协议,但参与者可以在需要时建模任意复杂的协议。协议与在HelloWorld类中实现它的行为绑定在一起。
 正如卡尔·休伊特所说,一个演员不是演员——没有人可以交谈会很孤独。我们需要另一个与迎宾互动的演员。让我们制作一个 HelloWorldBot来接收来自迎接者的回复,并发送一些额外的问候消息,然后收集这些回复,直到达到给定的最大数量的消息为止。
在这里插入图片描述

public class HelloWorldBot extends AbstractBehavior<HelloWorld.Greeted> {
    public static Behavior<HelloWorld.Greeted> create(int max) {
        return Behaviors.setup(context -> new HelloWorldBot(context, max));
    }

    private final int max;
    private int greetingCounter;

    private HelloWorldBot(ActorContext<HelloWorld.Greeted> context, int max) {
        super(context);
        this.max = max;
    }

    @Override
    public Receive<HelloWorld.Greeted> createReceive() {
        return newReceiveBuilder().onMessage(HelloWorld.Greeted.class, this::onGreeted).build();
    }

    private Behavior<HelloWorld.Greeted> onGreeted(HelloWorld.Greeted message) {
        greetingCounter++;
        getContext().getLog().info("Greeting {} for {}", greetingCounter, message.whom);
        if (greetingCounter == max) {
            return Behaviors.stopped();
        } else {
            message.from.tell(new HelloWorld.Greet(message.whom, getContext().getSelf()));
            return this;
        }
    }
}

 请注意该Actor是如何使用实例变量管理计数器的。由于actor实例一次只处理一条消息,因此不需要像synchronized或AtomicInteger这样的并发保护。
 第三个Actor生成了Greeter和HelloWorldBot,并开始它们之间的交互。

public class HelloWorldMain extends AbstractBehavior<HelloWorldMain.SayHello> {

    public static class SayHello {
        public final String name;

        public SayHello(String name) {
            this.name = name;
        }
    }

    public static Behavior<SayHello> create() {
        return Behaviors.setup(HelloWorldMain::new);
    }

    private final ActorRef<HelloWorld.Greet> greeter;

    private HelloWorldMain(ActorContext<SayHello> context) {
        super(context);
        greeter = context.spawn(HelloWorld.create(), "greeter");
    }

    @Override
    public Receive<SayHello> createReceive() {
        return newReceiveBuilder().onMessage(SayHello.class, this::onStart).build();
    }

    private Behavior<SayHello> onStart(SayHello command) {
        ActorRef<HelloWorld.Greeted> replyTo =
                getContext().spawn(HelloWorldBot.create(3), command.name);
        greeter.tell(new HelloWorld.Greet(command.name, replyTo));
        return this;
    }
}

 现在我们想要尝试这个Actor,所以我们必须启动一个Actor系统来托管它:

    public static void main(String[] args) {
        final ActorSystem<SayHello> system =
                ActorSystem.create(HelloWorldMain.create(), "hello");

        system.tell(new HelloWorldMain.SayHello("World"));
        system.tell(new HelloWorldMain.SayHello("Akka"));
    }

 我们从定义的HelloWorldMain行为启动一个Actor系统,并发送两个SayHello消息,这将启动两个单独的HelloWorldBot Actor和单个Greeter Actor之间的交互。
 应用程序通常由单个ActorSystem组成,每个JVM运行多个actor。
 控制台输出可能是这样的:

16:54:36.328 [hello-akka.actor.default-dispatcher-5] INFO com.qxq.learn.akka.demo5.HelloWorld - Hello World!
16:54:36.330 [hello-akka.actor.default-dispatcher-5] INFO com.qxq.learn.akka.demo5.HelloWorld - Hello Akka!
16:54:36.330 [hello-akka.actor.default-dispatcher-3] INFO com.qxq.learn.akka.demo5.HelloWorldBot - Greeting 1 for Akka
16:54:36.330 [hello-akka.actor.default-dispatcher-7] INFO com.qxq.learn.akka.demo5.HelloWorldBot - Greeting 1 for World
16:54:36.330 [hello-akka.actor.default-dispatcher-3] INFO com.qxq.learn.akka.demo5.HelloWorld - Hello World!
16:54:36.330 [hello-akka.actor.default-dispatcher-3] INFO com.qxq.learn.akka.demo5.HelloWorld - Hello Akka!
16:54:36.330 [hello-akka.actor.default-dispatcher-7] INFO com.qxq.learn.akka.demo5.HelloWorldBot - Greeting 2 for World
16:54:36.330 [hello-akka.actor.default-dispatcher-3] INFO com.qxq.learn.akka.demo5.HelloWorld - Hello World!
16:54:36.330 [hello-akka.actor.default-dispatcher-5] INFO com.qxq.learn.akka.demo5.HelloWorldBot - Greeting 2 for Akka
16:54:36.331 [hello-akka.actor.default-dispatcher-7] INFO com.qxq.learn.akka.demo5.HelloWorldBot - Greeting 3 for World
16:54:36.331 [hello-akka.actor.default-dispatcher-3] INFO com.qxq.learn.akka.demo5.HelloWorld - Hello Akka!
16:54:36.331 [hello-akka.actor.default-dispatcher-5] INFO com.qxq.learn.akka.demo5.HelloWorldBot - Greeting 3 for Akka

 上述代码流程大致如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值