Как справиться с несвежим akka.net удаленным iactorref

Я использую удаленных актеров в akka.net в Windows-сервисе, который работает долго. Я получаю IActorRef для удаленного субъекта, используя ActorSelection, и поддерживаю этот IActorRef в течение продолжительного периода времени в службе. IActorRef указывает на систему актера, работающую в другой службе Windows. Я понимаю, что перезапуск удаленного субъекта не сделает недействительным удаленный субъект ref. Однако, возможно, что в какой-то момент удаленная служба Windows может быть перезапущена, и IActorRef в вызывающей службе Windows станет недействительным.

Какова лучшая практика для обработки этого? Наивным подходом было бы использование ActorSelection для получения нового IActorRef каждый раз, когда я хочу сделать вызов удаленному субъекту. Это, очевидно, было бы неэффективно.

Другой подход может заключаться в том, чтобы просто обернуть каждый вызов, который я выполняю на этом IActorRef, в какой-то конверт обработки ошибок, который перехватывает исключения и получает новый IActorRef с использованием actorselection и повторных попыток? Или конверт может сделать тестовый вызов перед каждым фактическим вызовом, чтобы проверить, жив ли удаленный субъект, и если нет, получить новый актер.

Есть ли лучший способ?

2 ответа

Опция по умолчанию для обнаружения мертвых актеров Watch их (см. документацию). Когда один актер смотрит на другого, он получит Terminated сообщение, после того, как просмотренный актер становится мертвым или недоступным.

Наблюдение за завершенным сообщением предупредит систему о том, что удаленный субъект умер, но возникает вопрос, как именно отреагировать на удаленного субъекта. Предполагая, что актер получает IActorRef для удаленного актера через его конструктор, как он получит новый IActorRef для удаленного актера, когда он снова станет живым. Одним из способов может быть сбой актера и его делегирование родительскому актору, который затем получит новый IActorRef удаленному актору с помощью выбора актора. Проблема с этим, однако, заключается в том, что исходный выбор актера для удаленного актера мог иметь место в неакторном коде в корне композиции, где обычно происходило внедрение зависимости. Я полагаю, что вы могли бы обойти это, передавая делегату фабрики выбора акторов, который мог бы использоваться для восстановления удаленного IActorRef. Альтернативой, которую я предложил, является создание класса-оболочки, который реализует IActorRef с именем FaultTolerantActorRef.

Этот класс берет путь удаленного (или локального) субъекта в конструкторе и периодически делает выбор актора, чтобы получить обновление IActorRef для удаленного субъекта. Таким образом, если по какой-либо причине удаленный субъект умирает, вызовы FaultTolerantActorRef будут заканчиваться пустыми буквами, а удаленный субъект не работает. Однако, когда удаленный субъект в конечном итоге снова включается, вызовы FaultTolerantActorRef в конечном итоге достигнут вновь возрожденного удаленного субъекта без необходимости каких-либо явных действий со стороны вызывающего локального субъекта.

Существует метод Invalidate, который заставит FaultTolerantActorRef сделать новый выбор актера при следующем вызове. Предположительно, он может быть вызван субъектом в ответ на сообщение Ter прекращено от удаленного субъекта. Даже без вызова Invalidate, свежий выбор актера будет происходить на основе интервала обновления, переданного конструктору.

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);
        }
    }
}
Другие вопросы по тегам