我正在尝试设置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封死信。 经理开始监督
答案 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();
}
我认为这是一个很大的问题,因为这种方法不能保证演员在睡眠时间总是创造/解决。
我继续打开这个问题,因为我认为这不是一个正确的答案。