观察终止消息将提醒系统远程参与者已经死亡,但是存在如何完全响应已终止的远程参与者的问题。假设一个actor通过它的构造函数获得一个IActorRef给一个远程actor,那么当这个actor再次活跃时,如何获得一个新的IActorRef给远程actor。一种方法是让演员失败并委托给父代演员,然后通过演员选择获得一个新的IActorRef给远程演员。然而,问题在于,远程参与者的原始参与者选择可能发生在通常会发生依赖性注入的组合根中的非参与者代码中。我想你可以通过传递一个actor选择工厂委托来解决这个问题,它可以用来重建远程IActorRef。我想出的另一种方法是创建一个实现IActorRef的包装类,名为FaultTolerantActorRef。
该类在构造函数中使用远程(或本地)actor的路径,并定期执行一个actor选择以获得对远程actor的刷新IActorRef。这样,如果由于某种原因,远程参与者死亡时调用FaultTolerantActorRef将在远程参与者死亡时以死信结束。但是,当远程参与者最终重新联机时,对FaultTolerantActorRef的调用最终将到达新近重新启动的远程参与者,而不必对调用本地参与者采取任何明确的行动。
有一个Invalidate方法将强制FaultTolerantActorRef在下次调用时执行新的actor选择。这可能会被一个演员响应来自远程演员的终止消息而被调用。即使不调用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);
}
}
}
谢谢你的回答。当我从不在演员身边的一段代码向远程演员发送消息时,这将如何工作。例如,我使用actor选择器向远程actor获得IActorRef,并通过Ask()发送消息。由于调用系统本身没有任何演员,我不会让演员接收终止消息。我想我可以为此创建一个演员,但是有没有其他方式可以实现这一点,而无需创建这样一个专门的演员? – cfcal