2017-04-21 80 views
0

我想設置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封死信。 經理開始監督

回答

0

這裏的問題是一個競爭條件。無論您是否使用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',您就可以開始發送消息。這些可以傳遞給演員的郵箱,甚至可以在演員完全在另一個線程上創建之前。 –

-1

您需要使用解析器

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

0

我發現什麼是演員無法創建足夠快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(); 
} 

我認爲這是相當大的問題,因爲這種方式不能保證演員總是在睡眠時間創建/決心。

我一直打開這個問題,因爲我認爲它不能成爲一個正確的答案。