处理进入故障状态的持久 WCF 客户端
Posted
技术标签:
【中文标题】处理进入故障状态的持久 WCF 客户端【英文标题】:Handle persistent WCF client entering faulted state 【发布时间】:2011-07-24 18:23:22 【问题描述】:我们有一个从 Web 应用程序使用的 WCF 服务。我们使用的客户端是使用 Visual Studio“添加服务引用”选项生成的。由于它是一个网络应用程序,并且由于应用程序的性质可能会导致会话相对较短,我们选择在用户登录时创建客户端实例并在会话期间保留它,然后在会话结束时处理它。
这让我想到了我的问题 - 我们正在尝试确定处理客户端通道进入故障状态的最佳方式。在搜索了一些之后,我们想出了这个:
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 服务正常返回故障,并且客户端可以继续处理它的业务。是服务器没有响应还是什么? 在这种情况下,我们使用的是 ASP.NET 成员资格,但我们遇到了超出“userIsOnlineTimeWindow”属性的情况。显然在这种情况下,将用户重定向到登录页面是有意义的,但我们正在努力确保我们为可能进入错误状态的任何其他情况做好准备。 【参考方案1】:要回答您的问题,您可以像这样处理 ChannelFactory 属性的Faulted event:
client.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);
这应该允许您执行您需要执行的任何日志记录/清理。
作为一般建议,您不应在会话期间保持通道打开,因此请确保在完成后正确关闭通道(异常中止)。
此外,如果可能,请考虑不使用 Visual Studio 添加服务参考,或者至少清理它生成的代码/配置。我建议如果您想使用代理实现,请通过从ClientBase 派生来创建您自己的实现,或者使用ChannelFactory 实现。由于您提到了包装类,我建议您使用 ChannelFactory 并处理 Faulted event 以满足您的清理需求。
【讨论】:
感谢您的回答。我看到很多人都在谈论为每次通话打开和关闭客户频道,我想知道这样做的原因是什么? 原因是你的服务只能有这么多的并发调用和并发会话。因此,如果您的 Web 应用程序中的每个会话都有一个打开的会话,您将很快达到默认限制并需要更改它,并且性能会受到影响。查看此处以获取有关优化这些(和其他)设置的更多信息:msdn.microsoft.com/en-us/library/ee377061(v=bts.10).aspx 谢谢。我一直在阅读有关 ChannelFactory 的更多内容,看起来我们将朝着这个方向前进。 @BrandonZeider 很棒的发现,我们也在使用通道工厂实现,我对您建议不要使用添加服务参考的原因感兴趣。 当代理对象出现故障状态时方法没有调用.. ?【参考方案2】:尝试在客户端代理上处理 .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
代理。
【讨论】:
取消订阅Faulted
事件怎么样?应该在哪里做?以上是关于处理进入故障状态的持久 WCF 客户端的主要内容,如果未能解决你的问题,请参考以下文章
WCF 客户端代理在被 hangfire 工作人员服务使用时陷入故障状态