Akka.DI.Autofac没有创建演员

问题描述:

我想设置DI,然后是官方akka.net文档(http://getakka.net/docs/Dependency%20injection#autofac)。然而演员从未创造。以下我的代码出了什么问题?Akka.DI.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上午01时五十零分11秒] [线程0006] [阿卡:// DiTestSystem /用户/ Manager1/Worker1]来自akka的消息字符串:// DiTestSystem/deadLetters to akka:// DiTestSystem/user/Manager1/Worker1未送达。遇到1封死信。 经理开始监督

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

当使用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发送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演员。

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

+0

谢谢你的解释。 DI容器在程序向演员发送消息之前不会创建演员,是否正确? – ShootingStar

+0

当ActorOf被调用时,actor系统将开始创建actor的实例。这将发生在另一个线程上。只要您拥有'IActorRef',您就可以开始发送消息。这些可以传递给演员的邮箱,甚至可以在演员完全在另一个线程上创建之前。 –

您需要使用解析器

public class EnvironmentSetting 
{ 
    public static IContainer Container; 
    public static IDependencyResolver Resolver; 
} 

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

public class WorkerManager : UntypedActor 
{ 
    protected override void PreStart() 
    { 
     IActorRef actor = Context.ActorOf(EnvironmentSetting.Resolver.Create<Worker>(), "Worker1"); 
     Console.WriteLine($"Created actor: {actor.Path}"); 
    } 

    protected override void OnReceive(object message) 
    { 
     if (message is string) 
      Console.WriteLine("Manager start supervise"); 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     ContainerBuilder builder = new ContainerBuilder(); 
     builder.RegisterType<Worker>(); 
     builder.RegisterType<WorkerManager>(); 

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

     EnvironmentSetting.Container = builder.Build(); 
     EnvironmentSetting.Resolver = new AutoFacDependencyResolver(EnvironmentSetting.Container, system); 

     var manageRef = system.ActorOf(system.DI().Props<WorkerManager>(), "Manager1"); 

     manageRef.Tell("Hello"); 

     Thread.Sleep(200); 

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

     Console.ReadLine(); 
    } 
} 
+0

你的代码和OP代码有什么区别? – Szer

+0

在这段代码中,我使用了resolver来创建监督者actor的子actor actor实例,使用 Context.ActorOf(EnvironmentSetting.Resolver.Create (),“Worker1”); 和你可以使用监督策略的儿童演员 http://getakka.net/docs/concepts/supervision –

+0

我认为这个代码可能工作,但它会增加复杂性。我不确定这是否正确。 – ShootingStar

我发现什么是演员无法创建足够快Autofac。

当我添加Thread.Sleep(20),最后,正确地创建演员。

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(); 
} 

我认为这是相当大的问题,因为这种方式不能保证演员总是在睡眠时间创建/决心。

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