【问题标题】:Akka.Net Streams and remoting (Sink.ActorRefWithAck)Akka.Net 流和远程处理 (Sink.ActorRefWithAck)
【发布时间】:2018-03-19 09:58:09
【问题描述】:

我使用 Sink.ActorRefWithAck 使用 Akka.net Streams 进行了一个非常简单的实现:订阅者向发布者请求一个大字符串,发布者通过切片发送它。 它在本地 (UT) 工作得非常好,但不是远程。我不明白怎么了?具体来说:订阅者能够将请求发送给发布者,发布者以OnInit 消息进行响应,但OnInit.Ack 将永远不会返回给发布者。这个Ack 消息最终会变成一个死信

INFO  Akka.Actor.EmptyLocalActorRef - Message Ack from akka.tcp://OutOfProcessTaskProcessing@localhost:12100/user/Streamer_636568240846733287 to akka://OutOfProcessTaskProcessing/user/StreamSupervisor-0/StageActorRef-0 was not delivered. 1 dead letters encountered.

请注意,日志来自目标参与者,因此消息在正确的进程中处理。没有明显的路径错误。

查看没有处理此消息的发布者代码,我真的不知道我在做什么错:

    public static void ReplyWithStreamedString(IUntypedActorContext context, string toStream, int chunkSize = 2000)
    {
        Source<string, NotUsed> source = Source.From(toStream.SplitBy(chunkSize));
        source.To(Sink.ActorRefWithAck<string>(context.Sender, new StreamMessage.OnInit(),
                new StreamMessage.OnInit.Ack(),
                new StreamMessage.Completed(),
                exception => new StreamMessage.Failure(exception.Message)))
            .Run(context.System.Materializer());
    }

这是订阅者代码:

public static Task<string> AskStreamedString(this ICanTell self, object message, ActorSystem context, TimeSpan? timeout = null)
    {
        var tcs = new TaskCompletionSource<string>();
        if (timeout.HasValue)
        {
            CancellationTokenSource ct = new CancellationTokenSource(timeout.Value);
            ct.Token.Register(() => tcs.TrySetCanceled());
        }

        var props = Props.Create(() => new StreamerActorRef(tcs));
        var tempActor = context.ActorOf(props, $"Streamer_{DateTime.Now.Ticks}");

        self.Tell(message, tempActor);

        return tcs.Task.ContinueWith(task =>
        {
            context.Stop(tempActor);
            if(task.IsCanceled)
                throw new OperationCanceledException();
            if (task.IsFaulted)                    
                throw task.Exception.GetBaseException();
            return task.Result;
        });
    }

    internal class StreamerActorRef : ReceiveActor
    {
        readonly TaskCompletionSource<string> _tcs;

        private readonly StringBuilder _stringBuilder = new StringBuilder();

        public StreamerActorRef(TaskCompletionSource<string> tcs)
        {
            _tcs = tcs;
            Ready();
        }

        private void Ready()
        {
            ReceiveAny(message =>
            {
                switch (message)
                {
                    case StreamMessage.OnInit _:
                        Sender.Tell(new StreamMessage.OnInit.Ack());
                        break;
                    case StreamMessage.Completed _:
                        string result = _stringBuilder.ToString();
                        _tcs.TrySetResult(result);
                        break;
                    case string slice:
                        _stringBuilder.Append(slice);
                        Sender.Tell(new StreamMessage.OnInit.Ack());
                        break;
                    case StreamMessage.Failure error:
                        _tcs.TrySetException(new InvalidOperationException(error.Reason));
                        break;
                }
            });
        }
    }

有消息:

public class StreamMessage
{
        public class OnInit
        {
            public class Ack{}
        }

        public class Completed { }

        public class Failure
        {
            public string Reason { get; }

            public Failure(string reason)
            {
                Reason = reason;
            }
        }
    }

【问题讨论】:

    标签: akka-stream akka.net akka-remoting


    【解决方案1】:

    一般来说,使用 actor refs 的源和接收器并未设计为通过远程连接工作 - 它们不包括消息重试,如果某些流控制消息无法传入,这可能会导致系统死锁。

    您正在寻找的功能称为 StreamRefs(其工作方式与 actor refs 类似,但用于流),并将作为 v1.4 版本的一部分提供(有关更多信息,请参阅 github pull request详情)。

    【讨论】:

    • 好的,谢谢你的精确。我认为它值得在文档中提及。此外,我还使用一个简单的发布者 Actor 实现了一种解决方法。
    • Publisher actor 也很脆弱,因为它没有重试语义。因此,如果控制消息(即请求更多事件)不会被传递一次,您的发布者将永远不会发送任何更多事件,因为它没有收到这样做的请求。您最终会遇到死锁的流。
    • 对不起,我没有解释清楚:我使用标准的 ReceiveActor 作为发布者,即一个非常经典的实现
    猜你喜欢
    • 1970-01-01
    • 2018-03-01
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多