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

时间:2016-05-03 21:56:30

标签: c# .net events akka.net

我正在尝试使用Akka.net构建文件下载actor。它应该在下载完成时发送消息,但也会报告下载进度。

在.NET中,有一些类支持使用多个事件的异步操作。例如,WebClient.DownloadFileAsync有两个事件:DownloadProgressChangedDownloadFileCompleted

最好使用基于任务的异步版本并使用.PipeTo扩展方法。但是,我无法看到如何使用异步方法公开两个事件。与WebClient.DownloadFileAsync的情况一样。即使使用WebClient.DownloadFileTaskAsync,您仍然需要使用事件处理程序处理DownloadProgressChanged

我发现使用它的唯一方法是在创建actor时连接两个事件处理程序。然后在处理程序中,我向Self和发件人发送消息。为此,我必须从事件处理程序内部引用actor的一些私有字段。这对我来说是错误的,但我看不出另一条出路。

在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);
    } 

因此,下载开始时,会设置一些字段。此外,我确保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();
        });
    }

供参考,这里是整个演员,但我认为重要的内容直接在这篇文章中:https://gist.github.com/AaronLenoir/4ce5480ecea580d5d283c5d08e8e71b5

2 个答案:

答案 0 :(得分:2)

  

我必须从事件内部引用演员的一些私人领域   处理程序。这对我来说是错误的,但我看不出另一条出路。

     

在Actor中使用多个事件处理程序是否有更安全的方法?

对于具有内部状态的演员来说,没有任何内在错误,并且属于该州的成员提升在演员内部处理的事件。如果采用面向对象的方法,那就没错了。

唯一真正令人担忧的是,如果内部状态在多个文件下载请求之间混合,但我认为您当前的代码是合理的。

一个可能更可口的方法可能是将FileDownloadActor看作一个单独使用的actor,启动它,下载文件,将结果告诉发送者然后杀死actor。启动actor是一种廉价的操作,这完全避免了在多个下载请求之间共享内部状态的可能性。

除非您特别需要将下载排队以按当前代码顺序运行 - 但队列可以完全由另一个actor管理,并且仍然将下载actor视为临时。

答案 1 :(得分:0)

我不知道您是否是这种情况,但我看到人们将 Actor 视为简单的对象时将它们视为微服务。记住 Actor 有内部状态。

现在考虑可扩展性,您无法将消息扩展到分布式 Actor 系统中的一个 Actor。您发送给一个 Actor 的消息将在执行该 Actor 的节点中执行。

如果您想并行执行下载操作(例如),您可以按照 Patrick 所说的去做,并为每个下载操作创建一个 Actor,并且该 Actor 可以在任何可用节点中执行。