调用 Activator.GetObject 后检查对象是否存在

5
我正在开发一个被动复制的项目,在该项目中,服务器之间互相交换消息。每个服务器的位置都为其他服务器所熟知。
因此,当一个服务器启动时,它会检查其他可能还没有启动的服务器。当我调用Activator.GetObject方法时,它是唯一的方式来确定其他服务器是否已经关闭,通过调用对象上的一个方法,并期望得到一个IOException(就像下面的例子一样)。
try 
{
    MyType replica = (MyType)Activator.GetObject(
    typeof(IMyType),
    "tcp://localhost:" + location + "/Server");

    replica.ping();
}
catch (IOEXception){} // server is down

我经常这样做,大多数时候都可以工作(尽管速度较慢),但是有时在处理过程中会在一个叫做NegotiateStream.ProcessRead的方法上阻塞,我不明白为什么会这样...


你是不是想说:IMyType replica = (IMyType)Activator.GetObject(typeof(IMyType), "tcp://localhost:" + location + "/Server");? - AngelCastillo
在这种情况下并没有问题,MyType 实现了 IMyType 接口。正如我所说的,大多数情况下这是有效的,所以我不认为那可能是问题所在! - Manuel Reis
2个回答

3

当服务器宕机时,我发现超时总是很慢(使用TcpChannel,在.NET Remoting中无法正确设置超时时间)。下面是我如何使用Ping函数的解决方法(对于您的需求来说可能有点复杂,因此我将解释一下重要部分):

  [System.Diagnostics.DebuggerHidden] // ignore the annoying breaks when get exceptions here.
  internal static bool Ping<T>(T svr)
  {
     // Check type T for a defined Ping function
     if (svr == null) return false;
     System.Reflection.MethodInfo PingFunc = typeof(T).GetMethod("Ping");
     if (PingFunc == null) return false;

     // Create a new thread to call ping, and create a timeout of 5 secons
     TimeSpan timeout = TimeSpan.FromSeconds(5);
     Exception pingexception = null;
     System.Threading.Thread ping = new System.Threading.Thread(
        delegate()
        {
           try
           {
              // just call the ping function
              // use svr.Ping() in most cases
              // PingFunc.Invoke is used in my case because I use
              // reflection to determine if the Ping function is
              // defined in type T
              PingFunc.Invoke(svr, null);
           }
           catch (Exception ex)
           {
              pingexception = ex;
           }
        }
     );

     ping.Start(); // start the ping thread.
     if (ping.Join(timeout)) // wait for thread to return for the time specified by timeout
     {
        // if the ping thread returned and no exception was thrown, we know the connection is available
        if (pingexception == null)
           return true;
     }

     // if the ping thread times out... return false
     return false;
  }

希望注释可以解释我在这里做什么,但我会给您整个函数的详细说明。如果您不感兴趣,请跳到我解释ping线程的部分。

DebuggerHidden属性

我设置了DebuggerHidder属性,因为在调试时,在ping线程中可能会不断抛出异常,而且这是预期的。如果需要调试此函数,很容易将其注释掉。

为什么要使用反射和泛型类型

'svr'参数应该是一个具有Ping函数的类型。在我的情况下,服务器上实现了几个不同的可远程接口,其中有一个共同的Ping函数。这样,我只需调用Ping(svr),而无需转换或指定类型(除非在本地实例化对象为'object')。基本上,这只是为了语法方便。

Ping线程

您可以使用任何逻辑来确定可接受的超时时间,在我的情况下,5秒很好。我创建一个值为5秒的TimeSpan 'timeout',一个Exception pingexception,并创建一个新线程,试图调用'svr.Ping()',并将'pingexception'设置为调用'svr.Ping()'时抛出的任何异常。

一旦我调用'ping.Start()',我立即使用布尔方法ping.Join(TimeSpan)等待线程成功返回,或者如果线程在指定的时间内没有返回,则继续执行。但是,如果线程执行完毕但抛出异常,我们仍然不希望Ping返回true,因为与远程对象通信时出现了问题。这就是我使用'pingexception'确保调用svr.Ping()时没有发生任何异常的原因。如果最后'pingexception'为空,那么我就知道我可以安全地返回true。

哦,为了回答您最初提出的问题(...有时它在调用NegotiateStream.ProcessRead的方法时会阻塞,我无法理解为什么...),我从未能够解决.NET Remoting的超时问题,所以这种方法是我为我们的.NET Remoting需求开发和清理的。


我有一段时间没有查看Stack Overflow了,而且我的项目已经完成,所以我不会尝试你的解决方案...但是它是最完整和解释得最清楚的答案,所以我会将其标记为已接受! - Manuel Reis

1
我用了一个带有泛型的改进版本:
    internal static TResult GetRemoteProperty<T, TResult>(string Url, System.Linq.Expressions.Expression<Func<T, TResult>> expr)
    {
        T remoteObject = GetRemoteObject<T>(Url);
        System.Exception remoteException = null;
        TimeSpan timeout = TimeSpan.FromSeconds(5);
        System.Threading.Tasks.Task<TResult> t = new System.Threading.Tasks.Task<TResult>(() => 
        {
            try
            {
                if (expr.Body is System.Linq.Expressions.MemberExpression)
                {
                    System.Reflection.MemberInfo memberInfo = ((System.Linq.Expressions.MemberExpression)expr.Body).Member;
                    System.Reflection.PropertyInfo propInfo = memberInfo as System.Reflection.PropertyInfo;
                    if (propInfo != null)
                        return (TResult)propInfo.GetValue(remoteObject, null);
                }
            }
            catch (Exception ex)
            {
                remoteException = ex;
            }
            return default(TResult);
        });
        t.Start();
        if (t.Wait(timeout))
            return t.Result;
        throw new NotSupportedException();
    }

    internal static T GetRemoteObject<T>(string Url)
    {
        return (T)Activator.GetObject(typeof(T), Url);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接