简体   繁体   English

扩展 Akka.Net

[英]Scaling Akka.Net

We use the Akka.net Framework for highly scaling applications in the energy sector.我们将 Akka.net 框架用于能源领域的高度扩展应用程序。

We use Akka.net for various tasks, mostly in the following form:我们将 Akka.net 用于各种任务,主要采用以下形式:

var system=ActorSystem.Create("actorSystem");
var props=Props.Create<UpdateActor>();
               .WithRouter(new SmallesMailboxPool(100));
var actorRef=system.ActorOf(props,"UpdateActor");
foreach(var timerow in timeRowList)
   actorRef.Tell(timerow)

Unfortunately the Akka.net framework scales very badly in many cases.不幸的是,Akka.net 框架在许多情况下扩展性非常差。 The CPU load is only 12%. CPU 负载仅为 12%。 Obviously only one thread or a few threads are used.显然只使用了一个线程或几个线程。

How can you configure Akka.Net to use multiple threads for processing the actors?如何配置 Akka.Net 以使用多个线程来处理参与者?

This is an educated guess, but if you're using SmallestMailboxPool , keep in mind that it works pretty badly with non-blocking I/O and terribly with stashing.这是一个有根据的猜测,但如果您使用的是SmallestMailboxPool ,请记住它在非阻塞 I/O 中的工作非常糟糕,并且在存储时非常糟糕。

First thing is usually to check, if there are no blocking operations (like synchronous I/O, calling AsyncMethod().Result or Thread.Sleep ), which will block current thread, effectively preventing it from being used by other actors.首先通常是检查是否有阻塞操作(如同步 I/O,调用AsyncMethod().ResultThread.Sleep ),这将阻塞当前线程,有效防止它被其他actor 使用。

Another issue is very specific to smallest mailbox router, and it's related to stashing and persistent actors.另一个问题是针对最小邮箱路由器的,它与存储和持久参与者有关。

Stashing藏匿

Stashing is one of the popular ways to work with multi-step operations.存储是处理多步骤操作的流行方法之一。 This pattern can be represented as bellow.这种模式可以表示如下。

  public class MyActor : ActorBase, IWithUnboundedStash
  {
     public IStash Stash { get; set; }

     public Receive Active(State workUnit) => message =>
     {
        switch(message)
        {
           case DoWork: 
              // stash all messages not related to current work
              Stash.Stash(message); 
              return true;
           case WorkDone done:
              // when current unit of work is done, unstash pending messages
              Stash.UnstashAll();
              Become(Idle);
              return true;
        }
     };

     public bool Idle(object message)
     {
        switch(message)
        {
           case DoWork work: 
              StartWork(work.State);
              Become(Active(work.State)); //continue work in new behavior
              return true;
           default: 
              return false;
        }
     } 

     public bool Receive(object message) => Idle(message);
  }

This case is pretty common ie persistent actors use it during their recovery process.这种情况很常见,即持久参与者在恢复过程中使用它。 Problem is that, it's cleaning up the mailbox, which gives SmallestMailbox routers a false sense that this actor's mailbox is empty, while in practice it's just stashing all incoming message.问题是,它正在清理邮箱,这让SmallestMailbox路由器误以为这个参与者的邮箱是空的,而实际上它只是隐藏所有传入的消息。

This is also a reason why peristent actors should not be routed using SmallestMailbox routers !这也是为什么不应该使用SmallestMailbox路由器路由持久 actor的原因! Tbh.天。 I cannot think of any scenario where putting persistent actors behind router of any kind is a valid option.我想不出任何将持久角色放在任何类型的路由器后面都是有效选择的场景。

I think what you need to do is create an an actor coordinator class, and inside create a list/dictionary of actors.我认为您需要做的是创建一个演员协调器类,并在内部创建一个演员列表/字典。 then (for what i understand) they should be working in parallel after you tell the coordinator about new updates.然后(据我所知)在您将新更新告诉协调员后,他们应该并行工作。

public class UpdateCoordinator : ReceiveActor
{
    //all your update referenced
    private readonly Dictionary<int, IActorRef> _updates;

    public UpdateCoordinator()
    {
        _updates = new Dictionary<int, IActorRef>();

        //create the update reference
        Receive<UpdateMessage>(updateMessage =>
        {
            //add to the list of actors
            CreateUpdateReferenceIfNotExists(updateMessage.Identifier);

            IActorRef childUpdateRef = _updates[updateMessage.Identifier];

            //start your update actor
            childUpdateRef.Tell(updateMessage);
        });
    }

    private void CreateUpdateReferenceIfNotExists(int identifier)
    {
        if (!_updates.ContainsKey(identifier))
        {
            IActorRef newChildUpdateRef = Context.ActorOf(Props.Create(()=> new UpdateActor(identifier)), $"Update_{identifier}");
            _updates.Add(identifier, newChildUpdateRef);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM