Akka.DI.Autofac 没有创建演员

Akka.DI.Autofac did not create actor

我正在尝试设置 DI,然后是官方 akka.net 文档 (http://getakka.net/docs/Dependency%20injection#autofac)。然而,演员从不创造。以下我的代码有什么问题?

public class Worker: ReceiveActor
{
    public Worker()
    {
        Receive<string>(m => Console.WriteLine("Worker is working"));
    }
}

public class WorkerManager : ReceiveActor
{
    public WorkerManager()
    {
        Receive<string>(m => Console.WriteLine("Manager start supervise"));
    }

    protected override void PreStart()
    {
        Context.ActorOf(Context.DI().Props<Worker>(), "Worker1");
    }
}

class Program
{
    static void Main(string[] args)
    {
        ContainerBuilder builder = new ContainerBuilder();
        builder.RegisterType<Worker>();
        builder.RegisterType<WorkerManager>();

        var system = ActorSystem.Create("DiTestSystem");

        IContainer container = builder.Build();
        IDependencyResolver resolver = new AutoFacDependencyResolver(container, system);
        var manageRef = system.ActorOf(system.DI().Props<WorkerManager>(), "Manager1");

        manageRef.Tell("Hello");
        system.ActorSelection("/user/Manager1/Worker1").Tell("Hello");

        Console.ReadLine();
    }
}

当运行代码时,我得到了这个

[INFO][24/04/2017 1:50:11 AM][线程 0006][akka://DiTestSystem/user/Manager1/Worker1] 从 akka://DiTestSystem/deadLetters 到 akka:/ /DiTestSystem/user/Manager1/Worker1 未送达。遇到 1 个死信。 经理开始监督

我发现使用 Autofac 无法足够快地创建 Actor。

当我添加 Thread.Sleep(20) 时,最后,正确创建 actor。

static void Main(string[] args)
{
    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterType<WorkerManager>();
    builder.RegisterType<Worker>();

    var system = ActorSystem.Create("DiTestSystem");

    IContainer container = builder.Build();
    IDependencyResolver resolver = new AutoFacDependencyResolver(container, system);
    var manageRef = system.ActorOf(system.DI().Props<WorkerManager>(), "Manager1");

    Thread.Sleep(20); // ADDED THIS LINE
    manageRef.Tell("Hello");
    system.ActorSelection("/user/Manager1/Worker1").Tell("Hello");

    Console.ReadLine();
}

我认为这是一个很大的问题,因为这种方法不能保证演员在睡眠时间总是create/resolve。

我一直打开这个问题,因为我认为这不是一个正确的答案。

这里的问题是竞争条件。无论您是否使用 DI 容器创建 actor,都可能发生这种情况。

在使用 actor 系统时,您需要记住一切都是异步发生的。在将消息发送给 WorkerManager 参与者后,您的代码可以继续,但这并不意味着该消息实际上已被接收和处理。它实际上被发布到一个邮箱,供演员准备好后在不同的线程上处理。

最好不要尝试直接访问 Worker 演员,因为您有一个 WorkerManager 演员。就像在现实生活中,你通常会要求经理安排一些工作来完成,然后经理会依次决定需要哪些工人,并将必要的工作分配给他们。

Akka.NET 有一个 router feature 对这种情况很有用。我已经用一些额外的日志记录和路由器配置更新了您的示例代码。

void Main()
{
    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterType<Worker>();
    builder.RegisterType<WorkerManager>();
    var container = builder.Build();

    var system = ActorSystem.Create("DITestSystem");
    var resolver = new AutoFacDependencyResolver(container, system);

    var manager = system.ActorOf(system.DI().Props<WorkerManager>(), "Manager");
    Console.WriteLine("Program: Created Manager");

    for (int i = 0; i < 10; i++)
    {
        manager.Tell("Hello");
    }

    Console.ReadKey(true);
}

public class Worker : ReceiveActor
{
    public Worker()
    {
        Receive<string>(m => Console.WriteLine($"Worker {Context.Self.Path.Name} received: {m}"));
    }

    protected override void PreStart()
    {
        Console.WriteLine($"PreStart: {Context.Self.Path}");
    }
}

public class WorkerManager : ReceiveActor
{
    IActorRef worker;

    public WorkerManager()
    {
        Receive<string>(m =>
        {
            Console.WriteLine($"Manager received: {m}");
            worker.Tell(m);
        });
    }

    protected override void PreStart()
    {
        Console.WriteLine($"PreStart: {Context.Self.Path}");

        var props = Context.DI().Props<Worker>().WithRouter(new RoundRobinPool(5));
        worker = Context.ActorOf(props, "Worker");
    }
}

注意 WorkerManager 演员中的这一行,它为 Worker 演员配置路由器。

var props = Context.DI().Props<Worker>().WithRouter(new RoundRobinPool(5));

这将导致 ManagerActor 以循环方式将消息转发给 5 个子 Worker 参与者。消息路由已为您处理,因此无需直接与 Worker 参与者交互。

该示例现在还会在获得对 ManagerActor 的引用后立即向 ManagerActor 发送 10 条消息。

for (int i = 0; i < 10; i++)
{
    manageRef.Tell("Hello");
}

随着播放中的更多消息和一些日志记录,您可以看到事物的顺序不是确定性的。这是 运行.

的输出
Program: Created Manager
PreStart: akka://DITestSystem/user/Manager
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
PreStart: akka://DITestSystem/user/Manager/Worker/$b
PreStart: akka://DITestSystem/user/Manager/Worker/$d
PreStart: akka://DITestSystem/user/Manager/Worker/$f
Worker $b received: Hello
Worker $b received: Hello
Worker $f received: Hello
Worker $f received: Hello
PreStart: akka://DITestSystem/user/Manager/Worker/$c
Worker $c received: Hello
Worker $c received: Hello
PreStart: akka://DITestSystem/user/Manager/Worker/$e
Worker $d received: Hello
Worker $d received: Hello
Worker $e received: Hello
Worker $e received: Hello

这是另一个的输出。

Program: Created Manager
PreStart: akka://DITestSystem/user/Manager
PreStart: akka://DITestSystem/user/Manager/Worker/$b
PreStart: akka://DITestSystem/user/Manager/Worker/$c
PreStart: akka://DITestSystem/user/Manager/Worker/$d
PreStart: akka://DITestSystem/user/Manager/Worker/$f
PreStart: akka://DITestSystem/user/Manager/Worker/$e
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Worker $d received: Hello
Worker $e received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Manager received: Hello
Worker $f received: Hello
Worker $e received: Hello
Manager received: Hello
Worker $f received: Hello
Worker $b received: Hello
Worker $b received: Hello
Worker $c received: Hello
Worker $c received: Hello
Worker $d received: Hello

您可以看到,在第一个 运行 中,WorkerManager 在创建任何子 Worker actors 之前收到了所有消息。在第二个 运行 中,它在收到任何消息之前创建了所有子 Worker actor。

要记住的要点是,最好通过消息进行交流,而不是对事情何时发生做出假设。