简体   繁体   English

在 akka.net Actor 中安全地使用事件处理程序

[英]Using event handlers inside an akka.net Actor safely

I'm trying to build a file download actor, using Akka.net.我正在尝试使用 Akka.net 构建文件下载角色。 It should send messages on download completion but also report download progress.它应该在下载完成时发送消息,但也报告下载进度。

In .NET there are classes supporting asynchronous operations using more than one event.在 .NET 中有一些类支持使用多个事件的异步操作。 For example WebClient.DownloadFileAsync has two events: DownloadProgressChanged and DownloadFileCompleted .例如WebClient.DownloadFileAsync有两个事件: DownloadProgressChangedDownloadFileCompleted

Preferably, one would use the task based async version and use the .PipeTo extension method.最好是使用基于任务的异步版本并使用.PipeTo扩展方法。 But, I can't see how that would work with an async method exposing two events.但是,我看不出这将如何与公开两个事件的异步方法一起使用。 As is the case with WebClient.DownloadFileAsync .WebClient.DownloadFileAsync的情况一样。 Even with WebClient.DownloadFileTaskAsync you still need to handle DownloadProgressChanged using an event handler.即使使用WebClient.DownloadFileTaskAsync您仍然需要使用事件处理程序处理DownloadProgressChanged

The only way I found to use this was to hook up two event handlers upon creation of my actor.我发现使用它的唯一方法是在创建我的演员时连接两个事件处理程序。 Then in the handlers, I messages to Self and the Sender.然后在处理程序中,我向 Self 和 Sender 发送消息。 For this, I must refer to some private fields of the actor from inside the event handlers.为此,我必须从事件处理程序内部引用参与者的一些私有字段。 This feels wrong to me, but I cannot see another way out.这对我来说是错误的,但我找不到其他出路。

Is there a safer way to use multiple event handlers in an Actor?是否有更安全的方法在 Actor 中使用多个事件处理程序?

Currently, my solution looks like this (_client is a WebClient instance created in the constructor of the actor):目前,我的解决方案如下所示(_client 是在 actor 的构造函数中创建的WebClient实例):

    public void HandleStartDownload(StartDownload message)
    {
        _self = Self;
        _downloadRequestor = Sender;

        _uri = message.Uri;
        _guid = message.Guid;
        _tempPath = Path.GetTempFileName();

        _client.DownloadFileAsync(_uri, _tempPath);
    }

    private void Client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        var completedMessage = new DownloadCompletedInternal(_guid, _tempPath);
        _downloadRequestor.Tell(completedMessage);
        _self.Tell(completedMessage);
    }

    private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        var progressedMessage = new DownloadProgressed(_guid, e.ProgressPercentage);
        _downloadRequestor.Tell(progressedMessage);
        _self.Tell(progressedMessage);
    } 

So when the download starts, some fields are set.所以当下载开始时,一些字段被设置。 Additionally, I make sure I Become a state where further StartDownload messages are stashed, until the DownloadCompleted message is received by Self:此外,我确保我Become一个状态,其中隐藏了更多的StartDownload消息,直到 Self 收到DownloadCompleted消息:

    public void Ready()
    {
        Receive<StartDownload>(message => {
            HandleStartDownload(message);
            Become(Downloading);
        });
    }

    public void Downloading()
    {
        Receive<StartDownload>(message => {
            Stash.Stash();
        });
        Receive<DownloadCompleted>(message => {
            Become(Ready);
            Stash.UnstashAll();
        });
    }

For reference, here's the entire Actor, but I think the important stuff is in this post directly: https://gist.github.com/AaronLenoir/4ce5480ecea580d5d283c5d08e8e71b5作为参考,这里是整个 Actor,但我认为重要的东西直接在这篇文章中: https : //gist.github.com/AaronLenoir/4ce5480ecea580d5d283c5d08e8e71b5

I must refer to some private fields of the actor from inside the event handlers.我必须从事件处理程序内部引用参与者的一些私有字段。 This feels wrong to me, but I cannot see another way out.这对我来说是错误的,但我找不到其他出路。

Is there a safer way to use multiple event handlers in an Actor?是否有更安全的方法在 Actor 中使用多个事件处理程序?

There's nothing inherently wrong with an actor having internal state, and members that are part of that state raising events which are handled within the actor.具有内部状态的actor 以及作为该状态的一部分的成员引发在actor 内处理的事件,这本身并没有什么问题。 No more wrong than this would be if taking an OO approach.如果采用面向对象的方法,没有比这更错误的了。

The only real concern is if that internal state gets mixed between multiple file download requests, but I think your current code is sound.唯一真正关心的是内部状态是否在多个文件下载请求之间混合,但我认为您当前的代码是合理的。

A possibly more palatable approach may be to look at the FileDownloadActor as a single use actor, fire it up, download the file, tell the result to the sender and then kill the actor.一种可能更可口的方法可能是将FileDownloadActor视为一次性使用的 actor,启动它,下载文件,将结果告诉发送者,然后杀死 actor。 Starting up actors is a cheap operation, and this completely sidesteps the possibility of sharing the internal state between multiple download requests.启动 actor 是一种廉价的操作,这完全避免了在多个下载请求之间共享内部状态的可能性。

Unless of course you specifically need to queue downloads to run sequentially as your current code does - but the queue could be managed by another actor altogether and still treat the download actors as temporary.当然,除非您特别需要像当前代码一样将下载排队以按顺序运行 - 但队列可以完全由另一个演员管理,并且仍然将下载演员视为临时演员。

I don't know if that is your case, but I see people treating Actors as micro services when they are simply objects.我不知道您是否是这种情况,但我看到人们将 Actor 视为简单的对象时将它们视为微服务。 Remember Actors have internal state.记住 Actor 有内部状态。

Now think about scalability, you can't scale messages to one Actor in a distributed Actor System.现在考虑可扩展性,您无法将消息扩展到分布式 Actor 系统中的一个 Actor。 The messages you're sending to one Actor will be executed in the node executing that Actor.您发送给一个 Actor 的消息将在执行该 Actor 的节点中执行。

If you want to execute download operations in parallel (for example), you do as Patrick said and create one Actor per download operation and that Actor can be executed in any available node.如果您想并行执行下载操作(例如),您可以按照帕特里克所说的去做,并为每个下载操作创建一个 Actor,并且该 Actor 可以在任何可用节点中执行。

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

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