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

发布于 2024-10-28 09:36:03 字数 1213 浏览 5 评论 0 原文

我们有一个从 Web 应用程序使用的 WCF 服务。我们使用的客户端是使用 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
}

但是那味道很大。显然,我们可以通过使用新客户端并在每次调用时处理它来避免这种情况。这似乎没有必要,但如果这是正确的方式,那么我想这就是我们会选择的。那么,优雅地处理确定客户端是否处于故障状态然后采取措施的最佳方法是什么?我们真的应该每次通话都吸引新客户吗?

另一件需要记住的事情 - 客户端的实例化以及所有这些检查和处理都发生在客户端的包装类中。如果我们按照我们想要的方式执行此操作,那么它对应用程序本身是透明的 - 进行调用并处理其中的异常不需要那里的特殊代码。

We've got a WCF service that we're consuming from a web app. The client we're using was generated using the Visual Studio "Add Service Reference" option. Since it's a web app, and since the nature of the app is likely to lead to relatively short sessions, we've opted to create an instance of the client when a user logs in and keep it around for the life of the session, then handle disposing of it when the session is through.

This brings me to my question - we're trying to decide the best way to handle the client's channel entering a Faulted state. After searching around some, we've come up with this:

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

This, however, does not work due to the fact that, at least in our case, even if the service is down the client will show the Open state until you actually try to make a call using it, at which point it then enters the Faulted state.

So this leaves us to do something else. Another option we came up with was:

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
}

But that smells. Obviously, we could avoid this by using a new client and disposing of it for every call. That seems unnecessary, but if it's the right way then I guess that's what we'll opt for. So what is the best way to gracefully handle determining if the client is in a faulted state and then doing something about it? Should we really just be getting a new client for every call?

One other thing to keep in mind - the instantiation of the client and all of this checking and handling happens in a wrapper class for the client. If we do this the way we've intended to, it's transparent to the app itself - making calls and handling exceptions from them requires no special code there.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

深陷 2024-11-04 09:36:03

要回答您的问题,您可以处理 ChannelFactory 属性的 Faulted 事件像这样:

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

这应该允许您执行您需要执行的任何日志记录/清理。

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

另外,如果可能,请考虑不使用 Visual Studio 添加服务引用,或者至少清理它生成的代码/配置。我建议,如果您想使用代理实现,请通过从 ClientBase 派生来创建自己的代理实现,或使用 ChannelFactory 实现。既然您提到了包装器类,我建议您使用 ChannelFactory 并处理 故障事件满足您的清理需求。

To answer your question, you can handle the Faulted event of the ChannelFactory property like this:

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

That should allow you to perform whatever logging/cleanup that you need to do.

As a general recommendation, you should not leave the channel open for the duration of the session, so make sure you are closing the channel properly (aborting upon exception) after you're finished with it.

Also, if possible, consider NOT using the Visual Studio Add Service Reference, or at the very least cleaning up the code/config it generates. I recommend that if you want to use a proxy implementation, create your own by deriving from ClientBase, or use a ChannelFactory implementation. Since you mention a wrapper class, I would recommend that you use a ChannelFactory and handle the Faulted event for your cleanup needs.

不忘初心 2024-11-04 09:36:03

尝试在客户端代理上处理 .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 块中,甚至可能将其包装在重试调用 n 次的 while() 循环中,然后记录失败。 EG:

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 事件处理程序有机会重新创建客户端代理。

Try handling the .Faulted event on the client proxy, eg:

((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);
}

It should trigger the instant the channel faults, giving you a chance to re-open it.

You should still also wrap each call to a client method in a try-catch block, and perhaps even wrap that in a while() loop that retries the call n times, then logs a failure. EG:

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");

In my code I've gone as far as to use a ManualResetEvent to block the while() loop until the client_Faulted event handler has had a chance to re-create the client proxy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文