Akka.DI.Autofac并没有创造演员

时间:2017-04-21 02:06:09

标签: dependency-injection autofac akka.net

我正在尝试设置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] [Thread 0006] [akka:// DiTestSystem / user / Manager1 / Worker1]来自akka的消息字符串:// DiTestSystem / deadLetters到akka:/ / DiTestSystem / user / Manager1 / Worker1未送达。遇到1封死信。 经理开始监督

2 个答案:

答案 0 :(得分:1)

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

使用actor系统时,您需要记住所有事情都是异步发生的。在将消息发送给WorkerManager actor之后,您可以继续使用代码,但这并不意味着该消息实际上已被接收和处理。它实际上发布到一个邮箱,供actor在准备就绪时在另一个线程上进行处理。

最好不要直接访问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 actor中为Worker actor配置路由器的这一行。

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

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

该示例现在还在获得对它的引用后立即向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个演员之前已收到所有消息。在第二次运行中,它在接收任何消息之前创建了所有子Worker个actor。

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

答案 1 :(得分:0)

我发现使用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();
}

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

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