Akka型演员:探索接待员模式

在本文中,我们将探讨另一种Akka类型的模式。 这次,我们将向您展示如何使用接待员模式。 这是有关Akka型的系列文章的第三篇也是最后一篇文章。 其他两篇文章也可以在此站点上找到。 如果您还不了解Akka Typed,最好先阅读“使用Akka Typed的第一步”一文:

接待员模式背后的想法非常简单,ScalaDoc对此进行了解释:

/**
 * A Receptionist is an entry point into an Actor hierarchy where select Actors
 * publish their identity together with the protocols that they implement. Other
 * Actors need only know the Receptionist’s identity in order to be able to use
 * the services of the registered Actors.
 */
object Receptionist {
...

因此,基本上,客户只需要知道如何访问接待员,就可以从那里访问在接待员处注册的任何其他参与者。 因此,让我们首先创建几个演员,然后向接待员注册。 为此,我们定义了两个非常简单的参与者:

/**
   * Simple service and protocol. Does nothing special, just print out
   * the received message.
   */
  object FirstService {

    sealed trait FirstServiceMsg
    final case class FirstServiceMsg1(msg: String) extends FirstServiceMsg

    val behavior = Static[FirstServiceMsg] {
      case msg:FirstServiceMsg => println("First Service Receiver: " + msg)
    }
  }

  /**
   * Another simple service and protocol. Does nothing special, just print out
   * the received message.
   */
  object SecondService {

    sealed trait SecondServiceMsg
    final case class SecondServiceMsg1(msg: String) extends SecondServiceMsg

    val behavior = Static[SecondServiceMsg] {
      case msg:SecondServiceMsg => println("Second Service Receiver: " + msg)
    }
  }

没什么特别的,只有两个服务可以打印出收到的每条消息。 除了这两个服务,我们还创建了一个服务,它将充当这两个服务的客户端:

object SenderService {

    sealed trait SenderMsg
    final case class registerAddresses(firstServices: Set[ActorRef[FirstServiceMsg]], secondServices: Set[ActorRef[SecondServiceMsg]]) extends SenderMsg
    final case class sendMessage(msg: String) extends SenderMsg

    val behavior = Total[SenderMsg] {
      case registerAddresses(firstRefs, secondRefs) => {
        Static {
          case sendMessage(msg) => {
            firstRefs.foreach(_ ! FirstServiceMsg1(msg))
            secondRefs.foreach(_ ! SecondServiceMsg1(msg))
          }
        }
      }
      case _ => Same
    }
  }

如您所见,还有一个相当基本的参与者,一旦收到registerAddresses消息,它就会改变行为。 更改其行为后,它将根据sendMessage消息来调用已注册的服务。 现在我们如何将所有这些联系在一起?

为此,我们创建了另一个演员,为我们启动了该场景:

val scenario2 = {
    Full[Unit] {
      case Sig(ctx, PreStart) => {
        val receptionist = ctx.spawn(Props(Receptionist.behavior), "receptionist");

        // register three actors that can work with the FirstServiceMsg protocol
        val service1a = ctx.spawn(Props(FirstService.behavior), "service1a")
        val service1b = ctx.spawn(Props(FirstService.behavior), "service1b")
        val service1c = ctx.spawn(Props(FirstService.behavior), "service1c")

        // register three actors that can work with the SecondServiceMsg protocol
        val service2a = ctx.spawn(Props(SecondService.behavior), "service2a")
        val service2b = ctx.spawn(Props(SecondService.behavior), "service2b")
        val service2c = ctx.spawn(Props(SecondService.behavior), "service2c")

        // and the actor that will eventually send messages
        val sender = ctx.spawn(Props(SenderService.behavior),"sender")

        // define the service keys we'll use for registering
        val serviceKey1 = new ServiceKey[FirstServiceMsg] {}
        val serviceKey2 = new ServiceKey[SecondServiceMsg] {}

        // register the services with the receptionise
        val responseWrapperFirst = ctx.spawnAdapter[Registered[FirstServiceMsg]] {case _ =>}
        val responseWrapperSecond = ctx.spawnAdapter[Registered[SecondServiceMsg]] {case _ =>}

        receptionist ! Register(serviceKey1, service1a)(responseWrapperFirst)
        receptionist ! Register(serviceKey1, service1b)(responseWrapperFirst)
        receptionist ! Register(serviceKey1, service1c)(responseWrapperFirst)

        receptionist ! Register(serviceKey2, service2a)(responseWrapperSecond)
        receptionist ! Register(serviceKey2, service2b)(responseWrapperSecond)
        receptionist ! Register(serviceKey2, service2c)(responseWrapperSecond)

        // as a client we can now ask the receptionist to give us the actor references for services
        // that implement a specific protocol. We pass the result to the sender service. Ugly way
        // for now, but more to demonstrate how it works.
        val getListingWrapper = ctx.spawnAdapter[Listing[FirstServiceMsg]] {
          case firsts : Listing[FirstServiceMsg] => {
            val secondWrapper = ctx.spawnAdapter[Listing[SecondServiceMsg]] {
              case seconds : Listing[SecondServiceMsg] => {
                sender ! registerAddresses(firsts.addresses, seconds.addresses)
              }
            }
            receptionist ! Find[SecondServiceMsg](serviceKey2)(secondWrapper)
          }
        }
        // get message from the first lookup, and pass it to the adapter, which will look up the
        // second
        receptionist ! Find[FirstServiceMsg](serviceKey1)(getListingWrapper)

        // now wait a bit to make sure that through the receptionist we get a list of target actorrefs
        Thread.sleep(200)

        // these are sent to all the registered service implementations
        sender ! sendMessage("Hello1")
        sender ! sendMessage("Hello2")

        Same
      }
    }
  }

那是很多代码,但是很容易看到发生了什么。 我们要做的第一件事是使用ctx.spawn创建一组我们刚刚讨论过的类型的子参与者。 定义参与者后,我们需要向接待员注册FirstService和SecondService参与者。 为此,我们需要发送如下消息:

/**
   * Associate the given  with the given . Multiple
   * registrations can be made for the same key. Unregistration is implied by
   * the end of the referenced Actor’s lifecycle.
   */
  final case class Register[T](key: ServiceKey[T], address: ActorRef[T])(val replyTo: ActorRef[Registered[T]]) extends Command

在我们的示例中,我们通过以下方式进行操作:

// define the service keys we'll use for registering
        val serviceKey1 = new ServiceKey[FirstServiceMsg] {}
        val serviceKey2 = new ServiceKey[SecondServiceMsg] {}

        // register the services with the receptionise
        val responseWrapperFirst = ctx.spawnAdapter[Registered[FirstServiceMsg]] {case _ =>}
        val responseWrapperSecond = ctx.spawnAdapter[Registered[SecondServiceMsg]] {case _ =>}

        receptionist ! Register(serviceKey1, service1a)(responseWrapperFirst)
        receptionist ! Register(serviceKey1, service1b)(responseWrapperFirst)
        receptionist ! Register(serviceKey1, service1c)(responseWrapperFirst)

        receptionist ! Register(serviceKey2, service2a)(responseWrapperSecond)
        receptionist ! Register(serviceKey2, service2b)(responseWrapperSecond)
        receptionist ! Register(serviceKey2, service2c)(responseWrapperSecond)

如您所见,我们创建了一个适配器来处理Register消息的replyTo参数(在这种情况下,我们只是忽略响应)。 然后,我们使用这些适配器向接待员注册我们的服务参与者。 至此,我们已经向接待员注册了我们的服务,现在可以使用“查找”消息:

/**
   * Query the Receptionist for a list of all Actors implementing the given
   * protocol.
   */
  final case class Find[T](key: ServiceKey[T])(val replyTo: ActorRef[Listing[T]]) extends Command

..以获取特定类型的已注册演员列表:

// as a client we can now ask the receptionist to give us the actor references for services
        // that implement a specific protocol. We pass the result to the sender service. Ugly way
        // for now, but more to demonstrate how it works.
        val getListingWrapper = ctx.spawnAdapter[Listing[FirstServiceMsg]] {
          case firsts : Listing[FirstServiceMsg] => {
            val secondWrapper = ctx.spawnAdapter[Listing[SecondServiceMsg]] {
              case seconds : Listing[SecondServiceMsg] => {
                sender ! registerAddresses(firsts.addresses, seconds.addresses)
              }
            }
            receptionist ! Find[SecondServiceMsg](serviceKey2)(secondWrapper)
          }
        }
        // get message from the first lookup, and pass it to the adapter, which will look up the
        // second
        receptionist ! Find[FirstServiceMsg](serviceKey1)(getListingWrapper)

需要注意的是第99行:

sender ! registerAddresses(firsts.addresses, seconds.addresses)

在这里,我们将有关注册服务的信息发送给了作为客户的演员。

现在剩下要做的就是将sendMessage消息发送给发件人,并且应该将其发送给所有已注册的服务:

// these are sent to all the registered service implementations
        sender ! sendMessage("Hello1")
        sender ! sendMessage("Hello2")

现在运行此命令时,输出如下所示:

First Service Receiver: FirstServiceMsg1(Hello1)
First Service Receiver: FirstServiceMsg1(Hello1)
First Service Receiver: FirstServiceMsg1(Hello1)
Second Service Receiver: SecondServiceMsg1(Hello1)
First Service Receiver: FirstServiceMsg1(Hello2)
First Service Receiver: FirstServiceMsg1(Hello2)
Second Service Receiver: SecondServiceMsg1(Hello1)
First Service Receiver: FirstServiceMsg1(Hello2)
Second Service Receiver: SecondServiceMsg1(Hello1)
Second Service Receiver: SecondServiceMsg1(Hello2)
Second Service Receiver: SecondServiceMsg1(Hello2)
Second Service Receiver: SecondServiceMsg1(Hello2)

容易吧!

翻译自: https://www.javacodegeeks.com/2015/11/akka-typed-actors-exploring-the-receptionist-pattern.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值