Akka.net динамическое добавление ребенка


Есть сконфигурированный ActorSystem с акторами, организованными иерархически следующим образом:

/user
  /processes
    /process1
    /process2
    /process3

Для генерации этой схемы я использую следующий код C#:

// in entry point
IActorRef processesCoordinatorActorRef = ActorSystem.ActorOf(Props.Create<ProcessesCoordinatorActor>(), "processes");

// in ProcessesCoordinatorActor.cs:
IActorRef processOneActorRef = Context.ActorOf(Props.Create<ProccessActor>(), "process1");
IActorRef processTwoActorRef = Context.ActorOf(Props.Create<ProccessActor>(), "process2");
IActorRef processThreeActorRef = Context.ActorOf(Props.Create<ProccessActor>(), "process3");

Моя проблема в том, что я хочу добавить ребенка-актера к process1, process2 или process3 из кодаточки входа (вне ProcessActor). Но теперь мои руки связаны, потому что IActorRef скрывает от меня некий актерский экземпляр.

Как мне ее решить?

1 4

1 ответ:

Дочерний актер должен быть создан в Context родительского актора. Если вы хотите, чтобы это произошло извне, просто отправьте сообщение родителю и попросите его создать дочернего актора (например, CreateChildActor). Нет никакого способа создать актера в одном месте, а затем "усыновить" его как ребенка другого актера.

Как правило (и не зная, что вы на самом деле пытаетесь сделать), мое предчувствие заключается в том, что создание актеров вниз по иерархии из вашего кода верхнего уровня / точки входа не является хорошее направление для вас, чтобы спуститься. Вы должны позволить родительским актерам в иерархии выполнять свою работу по созданию и надзору за детьми.

Если вам нужно было запустить этот процесс из точки входа, вы могли бы отправить свой CreateChildActor в любую точку иерархии с помощью ActorSelection и/или разрешить его в IActorRef.

Вот пример кода, показывающий, как это можно сделать, и пусть подпроцесс уведомит координатора, как только он будет готов (также здесь в качестве скрипки):

using System;
using System.Threading;
using Akka.Actor;

namespace ProcessActors
{
    class Program
    {
        /// <summary>
        /// Top-level process coordinator actor.
        /// </summary>
        public class ProcessesCoordinatorActor : ReceiveActor
        {
            public ProcessesCoordinatorActor()
            {
                Receive<CreateChildActor>(createRequest =>
                {
                    Console.WriteLine("{0} creating child actor: {1}", Self.Path, createRequest.ChildName);

                    var child = Context.ActorOf(createRequest.ChildProps, createRequest.ChildName);
                    if (createRequest.ActorToNotify != null)
                        createRequest.ActorToNotify.Tell(new ReadyForWork(child));
                });

                Receive<ReadyForWork>(ready =>
                {
                    Console.WriteLine("Coordinator sees worker ready: {0}", ready.Worker.Path);
                });

                ReceiveAny(o =>
                {
                    Console.WriteLine("{0} received {1}", Self.Path, o);
                });

            }
        }

        /// <summary>
        /// Actor for a given process.
        /// </summary>
        public class ProcessActor : ReceiveActor
        {
            public ProcessActor()
            {
                Receive<CreateChildActor>(createRequest =>
                {
                    Console.WriteLine("{0} creating child actor: {1}", Self.Path, createRequest.ChildName);

                    var child = Context.ActorOf(createRequest.ChildProps, createRequest.ChildName);
                    if (createRequest.ActorToNotify != null)
                        createRequest.ActorToNotify.Tell(new ReadyForWork(child));
                });

                ReceiveAny(o =>
                {
                    Console.WriteLine("{0} received {1}", Self.Path, o);
                });
            }
        }

        /// <summary>
        /// Sub-process.
        /// </summary>
        public class SubprocessActor : ReceiveActor
        {
            public SubprocessActor()
            {
                ReceiveAny(o =>
                {
                    Console.WriteLine("{0} received {1}", Self.Path, o);
                });
            }
        }

        public static void Main(string[] args)
        {
            using (var system = ActorSystem.Create("MyActorSystem"))
            {

                Console.WriteLine("Starting up.");

                var coordinator = system.ActorOf(Props.Create(() => new ProcessesCoordinatorActor()), "processes");
                var processProps = Props.Create(() => new ProcessActor());

                // create process actors
                coordinator.Tell(new CreateNewProcess("process1", processProps));
                coordinator.Tell(new CreateNewProcess("process2", processProps));
                coordinator.Tell(new CreateNewProcess("process3", processProps));

                var subprocessProps = Props.Create(() => new SubprocessActor());

                // tiny sleep to let everything boot
                Thread.Sleep(TimeSpan.FromMilliseconds(50));

                // get handle to an actor somewhere down in the hierarchy
                var process1 = system.ActorSelection("/user/processes/process1").ResolveOne(TimeSpan.FromSeconds(1)).Result;

                // create subprocess of process1 and notify the coordinator of new subprocess actor
                process1.Tell(new CreateNewSubprocess("subprocess1", subprocessProps, coordinator));

                Console.ReadLine();
            }
        }

        #region Messages
        /// <summary>
        /// Command to create ChildProps actor and notify another actor about it.
        /// </summary>
        public class CreateChildActor
        {
            public CreateChildActor(string childName, Props childProps, IActorRef actorToNotify)
            {
                ChildName = childName;
                ActorToNotify = actorToNotify;
                ChildProps = childProps;
            }

            public CreateChildActor(string childName, Props childProps)
                : this(childName, childProps, null)
            {
            }

            public Props ChildProps { get; private set; }
            public string ChildName { get; private set; }
            public IActorRef ActorToNotify { get; private set; }
        }

        public class CreateNewProcess : CreateChildActor
        {
            public CreateNewProcess(string childName, Props childProps, IActorRef actorToNotify)
                : base(childName, childProps, actorToNotify)
            {
            }

            public CreateNewProcess(string childName, Props childProps)
                : this(childName, childProps, null)
            {
            }
        }

        public class CreateNewSubprocess : CreateChildActor
        {
            public CreateNewSubprocess(string childName, Props childProps, IActorRef actorToNotify)
                : base(childName, childProps, actorToNotify)
            {
            }

            public CreateNewSubprocess(string childName, Props childProps)
                : this(childName, childProps, null)
            {
            }
        }

        /// <summary>
        /// Report to another actor when ready.
        /// </summary>
        public class ReadyForWork
        {
            public ReadyForWork(IActorRef worker)
            {
                Worker = worker;
            }

            public IActorRef Worker { get; private set; }
        }

        #endregion

    }
}