Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking if an object exists after calling Activator.GetObject

I'm developing a project with passive replication where servers exchange messages among themselves. The locations of each server are well-known by every other server.

So, it may happen that when a server comes up, it will check the other servers, that may haven't come up yet. When I call Activator.GetObject, is it the only way to find out that other servers are down by invoking a method on the object, and expect an IOException (such as the example below)?

try 
{
    MyType replica = (MyType)Activator.GetObject(
    typeof(IMyType),
    "tcp://localhost:" + location + "/Server");

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

I do this and it works most of the times (even though is slow), but sometimes it blocks on a method called NegotiateStream.ProcessRead during the process, and I can't understand why...

like image 700
Manuel Reis Avatar asked Oct 04 '22 04:10

Manuel Reis


1 Answers

When a server is down, the timeout has always been slow for me (using a TcpChannel, which doesn't let you set the timeout properly in .NET Remoting). Below is a workaround for how I use my Ping function (it's likely a bit complex for your needs, so I'll explain the parts that matter for you):

  [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;
  }

Hopefully the comments explain what I do here, but I'll give you a breakdown of the whole function. If you're not interested, just skip down to where I explain the ping thread.

DebuggerHidden Attribute

I set the DebuggerHidder attribute because when debugging, exceptions can be thrown here constantly in the ping thread, and they are expected. It is easy enough to comment this out should debugging this function become necessary.

Why I use reflection and a generic type

The 'svr' parameter is expected to be a type with a Ping function. In my case, I have a few different remotable interfaces implemented on the server with a common Ping function. In this way, I can just call Ping(svr) without having to cast or specify a type (unless the remote object is instantiated as an 'object' locally). Basically, this is just for syntactical convenience.

The Ping Thread

You can use whatever logic you want to determine an acceptable timeout, in my case, 5 seconds is good. I create a TimeSpan 'timeout' with a value of 5 seconds, an Exception pingexception, and create a new thread that tries to call 'svr.Ping()', and sets 'pingexception' to whatever exception is thrown when calling 'svr.Ping()'.

Once I call 'ping.Start()', I immediately use the boolean method ping.Join(TimeSpan) to wait for the thread to return successfully, or move on if the thread doesn't return within the specified amount of time. However, if the thread finished executing but an exception was thrown, we still don't want Ping to return true because there was a problem communicating with the remote object. This is why I use the 'pingexception' to make sure that no exceptions occurred when calling svr.Ping(). If 'pingexception' is null at the end, then I know I'm safe to return true.

Oh and to answer the question you originally asked (....sometimes it blocks on a method called NegotiateStream.ProcessRead during the process, and I can't understand why...), I have never been able to figure out the timeout issues with .NET Remoting, so this method is what I've baked and cleaned up for our .NET Remoting needs.

like image 76
Bender the Greatest Avatar answered Oct 10 '22 02:10

Bender the Greatest