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

4
我正在尝试使用Akka.net构建文件下载Actor。它应在下载完成时发送消息,同时报告下载进度。
在.NET中,有支持使用多个事件进行异步操作的类。例如,WebClient.DownloadFileAsync有两个事件:DownloadProgressChanged和DownloadFileCompleted。
最好使用基于任务的异步版本并使用.PipeTo扩展方法。但是,我看不出如何使用公开两个事件的异步方法。就像WebClient.DownloadFileAsync这种情况一样。即使使用WebClient.DownloadFileTaskAsync,您仍然需要使用事件处理程序处理DownloadProgressChanged。
我发现唯一的方法是在创建我的actor时连接两个事件处理程序。然后,在处理程序中,我将消息发送到Self和Sender。为此,我必须从事件处理程序内部引用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);
    } 

当下载开始时,会设置一些字段。此外,我确保自己处于一种状态,即进一步的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();
        });
    }

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

2个回答

2

我必须在事件处理程序内部引用演员的一些私有字段。这让我感到不安,但我看不出其他办法。

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

演员具有内部状态和成员,这些成员是该状态的一部分,引发的事件在演员内部处理并不是本质上错误的。这与采用面向对象的方法没有什么不同。

唯一真正的问题是如果该内部状态在多个文件下载请求之间混合使用,但我认为您当前的代码是可靠的。

可能更受欢迎的方法是将 FileDownloadActor 视为单次使用的演员,启动它,下载文件,将结果告诉发送者,然后终止该演员。启动演员是一项廉价操作,这完全避免了在多个下载请求之间共享内部状态的可能性。

除非当然您特别需要按顺序排队下载,就像当前代码一样 - 但是队列可以由另一个演员管理,并且仍将下载演员视为临时的。


谢谢,我确实会采用单次使用的actor方法。我会创建另一个队列来排队,因为我想要每个主机一个下载队列。 - Aaron

0

我不知道这是否适用于您,但我看到有人将Actor视为微服务,而它们只是对象。请记住,Actor具有内部状态。

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

如果您想并行执行下载操作(例如),则可以像Patrick所说的那样为每个下载操作创建一个Actor,并且该Actor可以在任何可用节点上执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接