7

Lets say I have some commonly used by other actors service-layer actor. For example, an registry service that stores and retrieves domain objects:

case class DomainObject(id: UUID)

class Registry extends akka.actor.Actor {
  def receive: Receive = {
    case o: DomainObject => store(o) // save or update object

    case id: UUID => sender ! retrieve(id) // retrieve object and send it back
  }
}

I do not want to explicitly pass instance of such registry into all actors who may use it. Instead of it, I want them to be able to somehow 'locate' it.

For this I can think of two solutions:

  1. Identify message: each registry user actor knows registry actor name from some configuration and able to sent identification message to it. After AgentIdentity message is received back we are good to go:

    val registryName = ... // some name
    val registryId = ... // some id
    var registry = _
    
    def preStart() {
      context.actorSelection(registryName) ! Identify(registryId)
    }
    
    def receive: Receive = {
      case ActorIdentity(`registryId`, ref) => registry = ref
    }
    

    I do not like this way because right after user actor initialisation there is a phase when we do not know if there is a registry in system et all and thus do not know will we ever be able to operate or not.

  2. Akka Extensions: I can create an extension which would:

    a. create instance of Registry actor in given Actor System on initialization;

    b. return this actor to user who needs it via some method in Extension.

    object RegistryKey extends ExtensionKey[RegistryExtension]
    
    class RegistryExtesion(system: ExtendedActorSystem) extends RegistryKey {
      val registry = system.actorOf(Props[Registry], "registry")
    }
    

The question is: which method is better and are Akka Extesions can be used for this at all?

4

2 回答 2

3

蛋糕模式或依赖注入库(例如subcut )怎么样。

Derek Wyatt 在他的“Akka Concurrency”一书中提到了 DI,而不是使用过多的 actorFor 来查找演员: http ://www.artima.com/forums/flat.jsp?forum=289&thread=347118

于 2013-06-09T15:17:31.817 回答
3

我认为扩展想法是一个很好的想法,只要您的注册参与者总是在同一个ActorSystem.

或者,使用actorSelection(改编自Remote Lookup):

class RegistryClient extends Actor {
  val path = "/path/to/registry/actor"
  context.setReceiveTimeout(3.seconds)

  def sendIdentifyRequest(): Unit =
    context.actorSelection(path) ! Identify(path)

  def receive = {
    case ActorIdentity(`path`, Some(ref)) ⇒
      context.setReceiveTimeout(Duration.Undefined)
      context.become(active(ref))
    case ActorIdentity(`path`, None) ⇒
      throw new RuntimeException("Registry not found")
    case ReceiveTimeout ⇒ sendIdentifyRequest() 
  }

  def active(registry: ActorRef): Actor.Receive = {
    // use the registry
  }
}

这将适用于远程或本地演员。

让我们看看扩展解决方案。Actor 是异步创建的actorOf因此,如果参与者无法初始化, 您的扩展构造函数在调用时不会失败。

如果您想确定 actor 初始化失败,那么一种了解方法是告诉askactor 它将响应的内容和Await响应。如果actor没有响应,Await将会抛出一个。TimeoutException

class RegistryExtension(system: ExtendedActorSystem) extends Extension {
  val registry = system.actorOf(Props[Registry], "registry")
  implicit val timeout: Timeout = Timeout(500.millis)
  val f = registry ? "ping"    // Registry should case "ping" => "pong" 
  Await.result(f, 500.millis)  // Will throw a TimeoutException if registry fails 
                               // to respond
}

第一次TimeoutException调用时会抛出。RegistryExtension(system).registry

于 2013-06-09T14:59:44.050 回答