2016-12-22 57 views
1

我在akka.net中使用远程演员长期保持windows服务。我使用ActorSelection为远程actor创建一个IActorRef,并在服务中保持IActorRef长时间处于活动状态。 IActorRef指向在另一个Windows服务中运行的角色系统。 我明白,远程actor的重启不会使远程actor参数失效。但是,可以想象,远程Windows服务可能会在某个时间点重新启动,并且调用Windows服务中的IActorRef将失效。如何处理陈旧的akka​​.net远程iactorref

处理此问题的最佳做法是什么? 幼稚的方法是每次我想打电话给远程actor时,使用ActorSelection来获得一个新的IActorRef。这显然是低效的。

另一种方法可能是简单地将每个我在该IActorRef上进行的调用包装在某种错误处理信封中,以捕获异常并使用actors选择和重试获取新的IActorRef?或者信封可能在每次实际呼叫之前进行测试呼叫,以查看远程演员是否仍然活着,如果没有得到新演员参考。

有没有更好的办法?

回答

1

检测死亡演员的默认选项是Watch(参见documentation)。当一个演员观看另一个演员时,它将收到Terminated消息,一旦观看的演员死亡或无法到达。

+0

谢谢你的回答。当我从不在演员身边的一段代码向远程演员发送消息时,这将如何工作。例如,我使用actor选择器向远程actor获得IActorRef,并通过Ask()发送消息。由于调用系统本身没有任何演员,我不会让演员接收终止消息。我想我可以为此创建一个演员,但是有没有其他方式可以实现这一点,而无需创建这样一个专门的演员? – cfcal

0

观察终止消息将提醒系统远程参与者已经死亡,但是存在如何完全响应已终止的远程参与者的问题。假设一个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); 
     } 
    } 
}