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
演员。
要记住的要点是,最好与消息进行交流,而不是对事情发生的时间做出假设。
您需要使用解析器
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();
}
}
你的代码和OP代码有什么区别? – Szer
在这段代码中,我使用了resolver来创建监督者actor的子actor actor实例,使用 Context.ActorOf(EnvironmentSetting.Resolver.Create
我认为这个代码可能工作,但它会增加复杂性。我不确定这是否正确。 – 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();
}
我认为这是相当大的问题,因为这种方式不能保证演员总是在睡眠时间创建/决心。
我一直打开这个问题,因为我认为它不能成为一个正确的答案。
谢谢你的解释。 DI容器在程序向演员发送消息之前不会创建演员,是否正确? – ShootingStar
当ActorOf被调用时,actor系统将开始创建actor的实例。这将发生在另一个线程上。只要您拥有'IActorRef',您就可以开始发送消息。这些可以传递给演员的邮箱,甚至可以在演员完全在另一个线程上创建之前。 –