2

我在长期运行的 Windows 服务中使用 akka.net 中的远程演员。我使用 ActorSelection 为远程参与者获取了一个 IActorRef,并且我在服务中保持这个 IActorRef 活动的时间很长一段时间。IActorRef 指向在另一个 Windows 服务中运行的参与者系统。我知道远程参与者的重新启动不会使远程参与者参考无效。但是,可以想象远程 Windows 服务可能会在某个时候重新启动,并且调用 Windows 服务中的 IActorRef 会变得无效。

处理此问题的最佳做法是什么?一种天真的方法是每次我想调用远程参与者时使用 ActorSelection 来获取一个新的 IActorRef 。这显然是低效的。

另一种方法可能是简单地将我对该 IActorRef 所做的每个调用包装在某种错误处理信封中,该信封捕获异常并使用actorselection 和重试获得新的 IActorRef?或者信封可能会在每次实际调用之前进行测试调用,以查看远程参与者是否还活着,如果没有,则获取新的参与者参考。

有更好的办法吗?

4

2 回答 2

1

检测死演员的默认选项是Watch他们(参见文档)。当一个演员观看另一个演员时,它会收到Terminated消息,一旦被观看的演员变得死亡或无法访问。

于 2016-12-26T08:28:38.950 回答
0

观察 Terminated 消息将提醒系统远程参与者已经死亡,但有一个问题是如何准确地响应终止的远程参与者。假设一个actor通过其构造函数获得了一个远程actor的IActorRef,当它再次活跃时,该actor如何获取远程actor的一个新的IActorRef。一种方法是让actor失败并委托给父actor,然后父actor将通过actor选择获得一个新的IActorRef给远程actor。然而,这样做的问题是,远程参与者的原始参与者选择可能发生在组合根中的非参与者代码中,通常会发生依赖注入。我想你可以通过传递一个演员选择工厂委托来解决这个问题,该委托可用于重建远程 IActorRef。

此类在构造函数中获取远程(或本地)参与者的路径,并定期执行参与者选择以获取远程参与者的刷新 IActorRef。这样,如果由于某种原因远程参与者死亡,对 FaultTolerantActorRef 的调用将在远程参与者死亡时以死信告终。然而,当远程参与者最终再次上线时,对 FaultTolerantActorRef 的调用最终将到达新恢复的远程参与者,而无需对调用本地参与者采取任何显式操作。

有一个 Invalidate 方法将强制 FaultTolerantActorRef 在下一次调用时进行新的演员选择。这可能由一个actor调用,以响应来自远程actor的 Terminated 消息。即使不调用 Invalidate,也会根据传递给构造函数的刷新间隔进行新的 Actor 选择。

using Akka.Actor;
using System;
using Akka.Util;
using System.Threading;

namespace JA.AkkaCore
{
    public class FaultTolerantActorRef : IActorRef
    {
        public IActorRef ActorRef
        {
            get
            {
                if (!_valid || DateTime.Now.Ticks > Interlocked.Read(ref _nextRefreshTime))
                    RefreshActorRef();
                return _actorRef;
            }

        }

        public ActorPath Path
        {
            get
            {
                return ActorRef.Path;
            }
        }

        object _lock = new object();
        IActorRef _actorRef;
        volatile bool _valid;
        string _path;
        IActorRefFactory _actorSystem;
        private TimeSpan _requestTimeout;
        private TimeSpan _refreshInterval;
        //private DateTime _nextRefreshTime = DateTime.MinValue;
        private long _nextRefreshTime = DateTime.MinValue.Ticks;

        public FaultTolerantActorRef(IActorRefFactory actorSystem, IActorRef actorRef,
            TimeSpan refreshInterval = default(TimeSpan), TimeSpan requestTimeout = default(TimeSpan))
            : this(actorSystem, actorRef.Path.ToString(), refreshInterval, requestTimeout)
        {
            _actorRef = actorRef;
            _valid = true;
        }
        public FaultTolerantActorRef(IActorRefFactory actorSystem, string actorPath,
            TimeSpan refreshInterval = default(TimeSpan), TimeSpan requestTimeout = default(TimeSpan))
        {
            if (refreshInterval == default(TimeSpan))
                _refreshInterval = TimeSpan.FromSeconds(60);
            else
                _refreshInterval = refreshInterval;
            if (requestTimeout == default(TimeSpan))
                _requestTimeout = TimeSpan.FromSeconds(60);
            else
                _requestTimeout = requestTimeout;
            _actorSystem = actorSystem;
            _valid = false;
            _path = actorPath;
        }
        private void RefreshActorRef()
        {
            lock(_lock)
            {
                if (!_valid || DateTime.Now.Ticks > _nextRefreshTime)
                {
                    _actorRef = _actorSystem.ActorSelectionOne(_path, _requestTimeout);
                    Interlocked.Exchange(ref _nextRefreshTime,DateTime.Now.Ticks + _refreshInterval.Ticks);
                    _valid = true;
                }
            }
        }

        public void Invalidate()
        {
            _valid = false;
        }

        public void Tell(object message, IActorRef sender)
        {
            ActorRef.Tell(message, sender);
        }

        public bool Equals(IActorRef other)
        {
            return ActorRef.Equals(other);
        }

        public int CompareTo(IActorRef other)
        {
            return ActorRef.CompareTo(other);
        }

        public ISurrogate ToSurrogate(ActorSystem system)
        {
            return ActorRef.ToSurrogate(system);
        }

        public int CompareTo(object obj)
        {
            return ActorRef.CompareTo(obj);
        }
    }
}
于 2016-12-29T16:18:15.007 回答