【问题标题】:Using event handlers inside an akka.net Actor safely在 akka.net Actor 中安全地使用事件处理程序
【发布时间】:2016-05-03 21:56:29
【问题描述】:

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

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

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

我发现使用它的唯一方法是在创建我的演员时连接两个事件处理程序。然后在处理程序中,我向 Self 和 Sender 发送消息。为此,我必须从事件处理程序内部引用参与者的一些私有字段。这对我来说感觉不对,但我看不到其他出路。

有没有更安全的方法在一个 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();
        });
    }

作为参考,这是整个Actor,但我认为重要的东西直接在这篇文章中:https://gist.github.com/AaronLenoir/4ce5480ecea580d5d283c5d08e8e71b5

【问题讨论】:

    标签: c# .net events akka.net


    【解决方案1】:

    我必须从事件内部引用actor的一些私有字段 处理程序。这对我来说感觉不对,但我看不到其他出路。

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

    具有内部状态的参与者本质上没有任何问题,并且作为该状态一部分的成员引发在参与者内部处理的事件。如果采用 OO 方法,那就没有比这更糟糕的了。

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

    一种可能更容易接受的方法可能是将FileDownloadActor 视为单一用途的演员,启动它,下载文件,将结果告诉发送者,然后杀死演员。启动 Actor 是一项成本低廉的操作,这完全避免了在多个下载请求之间共享内部状态的可能性。

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

    【讨论】:

    • 谢谢,我确实会采取一次性演员的做法。因为我希望每个主机有一个下载队列,所以我会做一些其他的东西来排队。
    【解决方案2】:

    我不知道你的情况是否如此,但我看到人们将 Actor 视为微服务,而它们只是对象。记住 Actor 有内部状态。

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

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

    【讨论】:

      猜你喜欢
      • 2015-02-01
      • 1970-01-01
      • 2020-01-02
      • 2011-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多