简体   繁体   中英

Best way to handle a WCF timeout

I have a real time app that tracks assets around a number of sites across the country. As part of this solution I have 8 client apps that update a central server.

My question is that sometimes the apps lose connection to the central server and I am wondering what is the best way to deal with this? I know I could just increase the max send/receive times to deal with the timeout BUT I also want a graceful solution to deal with if the connection to the server is down:

For example I'm calling my services like this:

using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient())
{
    statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
}

I was thinking of adding a try/catch so...

using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient())
{
    try
    {
       statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
    }
    catch (TimeoutException timeout)
    {
       LogMessage(timeout);
    }
    catch (CommunicationException comm)
    {
       LogMessage(comm);
    }
}

Dealing it this way doesn't allow me to rerun the code without having a ton of code repeat. Any one got any suggestions?

EDIT: Looking into Sixto Saez and user24601 answers having an overall solution is better than dealing with timeouts on an individual exception level BUT... I'm was thinking that the below would solve my problem (but it would add a TON of extra code error handling):

void Method(int statusId)
{
     var statusRepository = new StatusRepositoryClient.StatusRepositoryClient()

      try
      {
         IsServerUp();
         statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
         statusRepository.Close(); 
      }            
      catch (Exception ex)
      {
            statusRepository.Abort();

            if (ex is TimeoutException || ex is CommunicationException)
            {
              LogMessage(timeout);
              Method(statusId);
            }
            else
            {
                throw new Exception(ex.Message + ex.InnerException);
            }
        }

  }
}

bool IsServerUp()
{
    var x = new Ping();
    var reply = x.Send(IPAddress.Parse("127.0.0.1"));

    if (reply == null)
    {
       IsServerUp();
    }
    else
    {
       if (reply.Status != IPStatus.Success)
       {
          IsServerUp();
       }
    }

    return true;
}

For starters I think your Wcf error handling is wrong. It should look like this

var statusRepository = new StatusRepositoryClient.StatusRepositoryClient();
try
{
    statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
    statusRepository.Close()
}
catch(Exception e)
{
   statusRepository.Abort();
   LogMessage(e);
   throw; //I would do this to let user know.
}

I would also re-throw the error to let the user know about the problem.

I have a two-pronged approach to verifying the server is up:

1) I have set up a 'PING' to the server every 5 seconds. The server responds with a 'PONG' and a load rating (low, medium, high, so the client can adjust its load on the server). If the client EVER doesn't receive a pong it assumes the server is down (since this is very low stress on the server - just listen and respond).

2) Random timeouts like the one you are catching are logged in a ConnectionMonitor class along with all successful connections. A single one of these calls timing out is not enough to consider the server down since some may be very processor heavy, or may just take a very long time. However, a high enough percentage of these will cause the application to go into server timeout.

I also didn't want to throw up a message for every single connection timeout, because it was happening too frequently to people who use poorer servers (or just some computer lying in their lab as a server). Most of my calls can be missed once or twice, but missing 5 or 6 calls are clearly going to cause instrusion.

When a server-timeout state happens, I throw up a little dialog box explaining what's happening to the user.

Prior to designing your exception handling, one important decision to make is whether you want guaranteed delivery of each message the client sends or is it OK for the service to "lose" some. For guaranteed delivery, the best built-in solution is the netMsmqBinding assuming the client can be configured to support it. Otherwise, there is a lightweight reliable messaging capability built into WCF. You'll be going down a rabbit hole if you try to handle message delivery purely through exception handling... :)

Hi Please see my solution below. Also please note that the below code has not been compliled so may have some logic and typing errors.

bool IsServerUp()
{
    var x = new Ping();
    var reply = x.Send(IPAddress.Parse("127.0.0.1"));

if (reply == null) return false;

return reply.Status == IPStatus.Success ? true : false;
} 

int? GetStatusId()
{
try 
{
    using (var statusRepository = new  StatusRepositoryClient.StatusRepositoryClient())
    {
        return statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
    }
}catch(TimeoutException te)
{
    //Log TimeOutException occured
    return null;
}
}

void GetStatus()
{
try
{
    TimeSpan sleepTime = new TimeSpan(0,0,5);
    int maxRetries = 10;

    while(!IsServerUp())
    {
        System.Threading.Thead.Sleep(sleepTime);
    }

    int? statusId = null;
    int retryCount = 0;

    while (!statusId.HasValue)
    {
        statusId = GetStatusId();
        retryCount++;

        if (retryCount > maxRetries)
            throw new ApplicationException(String.Format("{0} Maximum Retries reached in order to get StatusId", maxRetries));
        System.Threading.Thead.Sleep(sleepTime);
    }
}catch(Exception ex)
{
    //Log Exception Occured
}
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM