处理持久化的WCF客户端进入故障状态。

19
我们有一个WCF服务,在Web应用程序中使用。我们使用了Visual Studio的“添加服务引用”选项生成了客户端。由于是Web应用程序,并且应用程序的性质可能导致会话相对较短,因此我们选择在用户登录时创建客户端实例,并将其保留到会话结束,然后处理其释放。
这带来了我的问题——我们正在尝试确定处理客户端通道进入故障状态的最佳方法。在进行一些搜索后,我们想到了以下解决方案:
if(client.State = CommuncationState.Faulted)
{
    client = new Client();
}

try
{
    client.SomeMethod();
}
catch //specific exceptions left out for brevity
{
    //logging or whatever we decide to do
    throw;
}

然而,由于在我们的情况下,即使服务已经停止,客户端仍会显示“Open”状态,直到你真正尝试使用它进行调用,此时它才进入“Faulted”状态,因此此方法无效。

所以这让我们想到其他的解决方案。我们提出了另一种选择:

try
{
    client.SomeMethod();
}
catch
{
    if(client.State == CommunicationState.Faulted)
    {
        //we know we're faulted, try it again
        client = new Client();
        try
        {
            client.SomeMethod();
        }
        catch
        {
            throw;
        }
    }
    //handle other exceptions
}

但这样会有异味。显然,我们可以通过每次调用时使用新客户端并处理掉它来避免这种情况。那似乎是不必要的,但如果这是正确的方法,那么我想这就是我们要选择的方法。那么优雅地处理确定客户端是否处于失效状态,然后采取措施的最佳方法是什么?我们真的应该为每个调用获取一个新的客户端吗?

还有一件事要记住 - 客户端的实例化以及所有这些检查和处理都发生在客户端的包装类中。如果我们按照我们的意图这样做,它对应用程序本身是透明的 - 调用和处理异常不需要任何特殊的代码。


客户端为什么会进入故障状态?我一直能够让WCF服务正常返回故障,客户端可以继续进行业务。是服务器没有响应还是其他原因? - Tridus
在这种情况下,我们正在使用ASP.NET成员资格,并通过超过“userIsOnlineTimeWindow”属性遇到了问题。显然,在这种情况下,将用户重定向到登录页面是有意义的,但我们正在努力确保我们为可能遇到故障状态的任何其他情况做好准备。 - Zann Anderson
2个回答

20

回答你的问题,你可以像这样处理 ChannelFactory 属性的 Faulted event

client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);

这将允许您执行所需的任何日志记录/清理操作。

一般建议,您不应在会话期间保持通道开放,因此请确保在完成后正确关闭通道(在异常时中止)。

此外,如果可能,请考虑不使用Visual Studio添加服务引用,或者至少清理它生成的代码/配置。我建议如果您想使用代理实现,则通过继承ClientBase创建自己的实现,或使用ChannelFactory实现。由于您提到了包装类,我建议您使用ChannelFactory并处理Faulted event以满足您的清理需求。


谢谢你的回答。我看到很多人都在谈论为每个调用打开和关闭客户端通道,我想知道这样做的原因是什么? - Zann Anderson
2
原因是您的服务只能有那么多并发调用和会话。因此,如果 Web 应用程序中的每个会话都有一个打开的会话,您将很快达到默认限制并需要更改它,性能将受到影响。请查看以下链接以获取有关优化这些(和其他)设置的更多信息: http://msdn.microsoft.com/en-us/library/ee377061(v=bts.10).aspx - BrandonZeider
谢谢。我已经阅读了更多关于ChannelFactory的内容,看起来我们会朝这个方向前进。 - Zann Anderson
@BrandonZeider 很好的发现,我们也在使用通道工厂实现,我对你建议不使用添加服务引用的原因很感兴趣。 - TJB
当代理对象进入故障状态时,方法没有被调用..? - Jenish Zinzuvadiya

13

尝试在客户端代理上处理 .Faulted 事件,例如:

((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted);

private void client_Faulted(object sender, EventArgs e)
{
    client = new Client();
    ((ICommunicationObject)client).Faulted += new EventHandler(client_Faulted);
}

当频道出现故障时,它应该会立即触发以便让你有机会重新打开它。

你仍然应该在每次调用client方法时包含一个try-catch块,并可能将其包装在一个while()循环中,以重试n次调用,然后记录失败。例如:

bool succeeded = false;
int triesLeft = 3;
while (!succeeded && triesLeft > 0)
{
    triesLeft--;
    try
    {
        client.SomeMethod();
        succeeded = true;
    }
    catch (exception ex)
    {
        logger.Warn("Client call failed, retries left: " + triesLeft;
    }
}
if (!succeeded)
    logger.Error("Could not call web service");

在我的代码中,我已经使用了一个ManualResetEvent来阻塞while()循环,直到client_Faulted事件处理程序有机会重新创建client代理。


2
取消订阅“Faulted”事件应该在哪里完成? - itsho

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