Как печатать всех актеров в системе акка?

Я создал систему akka. Предположим, что в нем есть несколько действующих лиц. Как я могу печатать всех актеров из системы akka с их путем? (для целей отладки)

Ответ 1

Этот ответ Роланда Кун предполагает, что это не совсем тривиальная проблема, но вы можете приблизиться (для актеров, которые ответят к сообщениям в разумные сроки), используя протокол Identify - ActorIdentity, который подчиняется всем участникам.

Некорректный код, сложенный вместе, чтобы проиллюстрировать идею:

import akka.actor._

class IdentifyingActor extends Actor with ActorLogging {

  def receive = {

    case 'start =>
      self ! ActorPath.fromString("/user")

    case path: ActorPath =>
      context.actorSelection(path / "*") ! Identify(())

    case ActorIdentity(_, Some(ref)) =>
      log.info(ref.path)
      self ! ref.path
}

Ответ 2

ActorSystem имеет закрытый метод printTree, который можно использовать для отладки.

1) Частный вызывающий метод (от https://gist.github.com/jorgeortiz85/908035):

class PrivateMethodCaller(x: AnyRef, methodName: String) {
  def apply(_args: Any*): Any = {
    val args = _args.map(_.asInstanceOf[AnyRef])

    def _parents: Stream[Class[_]] = Stream(x.getClass) #::: _parents.map(_.getSuperclass)

    val parents = _parents.takeWhile(_ != null).toList
    val methods = parents.flatMap(_.getDeclaredMethods)
    val method = methods.find(_.getName == methodName).getOrElse(throw new IllegalArgumentException("Method " + methodName + " not found"))
    method.setAccessible(true)
    method.invoke(x, args: _*)
  }
}

class PrivateMethodExposer(x: AnyRef) {
  def apply(method: scala.Symbol): PrivateMethodCaller = new PrivateMethodCaller(x, method.name)
}

2) Использование

val res = new PrivateMethodExposer(system)('printTree)()
println(res)

Будет напечатан:

-> / LocalActorRefProvider$$anon$1 class akka.actor.LocalActorRefProvider$Guardian status=0 2 children
   ⌊-> system LocalActorRef class akka.actor.LocalActorRefProvider$SystemGuardian status=0 3 children
   |   ⌊-> deadLetterListener RepointableActorRef class akka.event.DeadLetterListener status=0 no children
   |   ⌊-> eventStreamUnsubscriber-1 RepointableActorRef class akka.event.EventStreamUnsubscriber status=0 no children
   |   ⌊-> log1-Logging$DefaultLogger RepointableActorRef class akka.event.Logging$DefaultLogger status=0 no children
   ⌊-> user LocalActorRef class akka.actor.LocalActorRefProvider$Guardian status=0 1 children
...

Остерегайтесь, это может вызвать OOM. Если у вас много актеров.

Ответ 3

В соответствии с документацией вы можете использовать ActorSelection с подстановочным знаком *, чтобы заставить участников отправлять идентифицирующие сообщения. У вас может быть актер, который собирает ActorRef s.

Как упоминалось @chris-martin, только те, кто в данный момент не занят, отправят. Очень простой актер:

// make all the available actor to send an identifying message
public void freeActors()
{
  ActorSelection selection =
    getContext().actorSelection("/user/*");
  selection.tell(new Identify(identifyId), getSelf());
}

...
// collect responses
@Override
public void onReceive(Object message) {
  if (message instanceof ActorIdentity) {
    ActorIdentity identity = (ActorIdentity) message;
    // get the ref of the sender 
    ActorRef ref = identity.getRef();
    // the sender is up and available
   ...

EDIT: Я знаю, что это для Java, но мне это показалось полезным.