处理WCF超时的最佳方法

8
我有一个实时应用程序,可以跟踪全国各地多个站点的资产。作为这个解决方案的一部分,我有8个客户端应用程序更新一个中央服务器。
我的问题是有时应用程序会失去与中央服务器的连接,我想知道最好的处理方式是什么?我知道我可以增加最大发送/接收时间来处理超时,但我也希望有一个优雅的解决方案来处理如果与服务器的连接断开:
例如,我像这样调用我的服务:
using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient())
{
    statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
}

我考虑添加一个try/catch,以便...
using (var statusRepository = new StatusRepositoryClient.StatusRepositoryClient())
{
    try
    {
       statusId = statusRepository.GetIdByName(licencePlateSeen.CameraId.ToString());
    }
    catch (TimeoutException timeout)
    {
       LogMessage(timeout);
    }
    catch (CommunicationException comm)
    {
       LogMessage(comm);
    }
}

这种处理方式不能让我重新运行代码,因为会有大量的重复代码。有人有什么建议吗?
编辑:研究了Sixto Saez和user24601的答案后,有一个总体解决方案比在单个异常级别上处理超时更好,但是......我认为下面的方法可以解决我的问题(但这将增加大量额外的错误处理代码):
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;
}

你可以编写一个函数来测试服务器是否正常运行,这样在连接到存储库之前就可以检查服务器是否正常运行。你也可以进入循环直到服务器正常运行。 - Jethro
那么编写一个递归函数,直到服务器启动后才退出,然后继续...并在任何wcf调用之前弹出该调用?我喜欢这个想法,它比我上面提到的编辑代码更少。您的想法绝对可以提高可靠性99%,但如果服务器在检查和方法调用之间失去连接,我该怎么处理呢? - Jon Jones
@Jethro:最佳实践建议不要使用ping方法。请参见此处的讨论:http://stackoverflow.com/questions/2166356/how-to-check-if-a-wcf-service-is-operational 基本思路是,超时可能是服务依赖关系的结果,在ping事件中不会显示出来(例如,一个与数据库交互的WCF服务,其中数据库导致超时)。 - VoteCoffee
4个回答

3

首先,我认为您的WCF错误处理方式不正确。它应该像这样:

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.
}

我建议重新抛出错误,以便让用户知道问题所在。


我处理关闭/中止操作相比让垃圾收集器在using语句完成后自动触发有什么好处? - Jon Jones
1
这里有一篇很好的文章,讲述了WCF中Dispose模式的实现以及为什么不应该在其中使用using语句。文章链接:http://geekswithblogs.net/DavidBarrett/archive/2007/11/22/117058.aspx - Sixto Saez

2
在设计异常处理之前,要做出一个重要的决定,即您是否希望保证客户端发送的每个消息都能够传递成功,还是允许服务“丢失”一些消息。如果需要保证传递成功,则最好使用内置的netMsmqBinding解决方案,前提是客户端可以配置支持它。否则,WCF内置了轻量级可靠消息传递能力。如果您试图纯粹通过异常处理来处理消息传递,则会陷入一个死胡同... :)

2

我有两种方法来验证服务器是否正常:

1) 我建立了一个 "PING" 任务,每隔5秒钟向服务器发送一次。服务器会回应一个 "PONG" 并带有一个负载等级(低、中、高),以便客户端可以调整其在服务端的负载。如果客户端一直没有收到"PONG",则认为服务器已经宕机(因为这对服务器来说是非常低压力的操作 - 只需要监听并响应即可)。

2) 像您所捕捉到的随机超时事件将被记录在 ConnectionMonitor 类中,连同所有成功连接一起。单个这样的超时调用不足以认定服务器宕机,因为有些可能需要非常高的处理器负载或可能只需要很长时间。然而,这种情况发生的百分比越高,应用程序就越容易进入服务器超时状态。

我也不想为每个连接超时都弹出一个消息,因为对于使用较差服务器(或只是作为实验室中的计算机)的人来说,这种情况发生得太频繁了。我的大部分调用可以漏掉一两次,但是漏掉5或6次显然会导致干扰。

当发生服务器超时状态时,我会弹出一个小对话框,向用户解释发生了什么。


1

你好,请查看下面的解决方案。请注意,下面的代码尚未编译,可能存在一些逻辑和类型错误。

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

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