如何防止 WCF 服务进入故障状态?

Posted

技术标签:

【中文标题】如何防止 WCF 服务进入故障状态?【英文标题】:How do I prevent a WCF service from enter a faulted state? 【发布时间】:2010-09-23 20:44:21 【问题描述】:

我有一个不应进入故障状态的 WCF 服务。如果出现异常,则应将其记录下来,并且服务应继续不间断。该服务具有单向操作契约,并且正在从 MSMQ 读取消息。

我的问题是双重的:

    服务似乎正在吞食 异常/故障,所以我无法 调试它。我如何获得服务 公开异常,以便我 可以记录或处理吗? 服务是 后进入故障状态 这个例外被吞没了。怎么做 我阻止服务进入 进入故障状态?

【问题讨论】:

这里你可以得到所有的理论msdn.microsoft.com/en-us/library/ms789041(v=vs.110).aspx 【参考方案1】:

关于如何处理故障的官方文档在这里:

Handling Exceptions and Faults

Understanding State Changes

主页位于Channel Model Overview

有一个很好的状态图显示了事情是如何发生的:

【讨论】:

【参考方案2】:

大多数(如果不是全部)异常都可以在 WCF 跟踪 (Configuring Tracing) 中看到,最好使用 Service Trace Viewer 查看跟踪。

显然,这不应该在生产环境中整天运行,但无论如何它有助于排除故障。

除此之外,请注意,根据您使用的 SessionMode,oneways 可能不会作为真正的“即发即弃”运行。如果您将服务配置为 SessionMode.Allowed 甚至 SessionMode.Required,则 oneway 操作将像根本不是 oneway 一样运行(在 netTcpBinding 上使用 oneway 时可以观察到这一点)。然而,坦率地说,我不知道这是否会改变您可以获得的异常类型,或者何时获得它们。但是,无论如何,如果根本无法发送请求,您应该得到一个异常。 AFAIK,当它在服务器端成功排队时,单向“结束”。因此,在此之前(想到序列化/反序列化),(与 WCF 框架相关的)异常仍有一些地方。

然后,使用上述跟踪/跟踪查看器可以最好地查看此类与框架相关的异常(即使是 IErrorHandler 也无法全部获取,因为在请求/响应流中调用它时)。

【讨论】:

我面临着类似的问题。我的服务在生产上运行,netTcpBinding 的方法很少,如 IsOneway=True 和 SessionMode 设置为必需。但是当发生异常时,服务进入故障状态.要让它工作,我必须重新启动它【参考方案3】:

异常会使代理出错。 AFAIK 对此无能为力:不要引发异常;-p

我有点惊讶单向仍然会导致问题,但对于吞咽总的来说l,有3个方面:

    你在扔faults吗?还是例外?这很重要(并且应该是“错误”) 作为 hack,您可以启用调试异常消息 - 但请关闭它!!!

    你在“使用”服务对象吗?我刚刚blogged 在这个确切的主题上......基本上,你的“使用”可以吞下这个例外。 3 个选项:

    不要使用“使用” 子类化代理并覆盖 Dispose() 按照博客进行包装

【讨论】:

感谢 cmets Marc。我停止使用 using 来尝试追踪异常,但没有运气。 WCF 服务调用的业务层正在抛出异常,但这些异常正在某处被吞没......神秘...... 您可以尝试切换到 FaultException ,其中 T 是已发布的错误 - 这可能会有所帮助。并且注意关闭代理还是很重要的;在这种情况下,“使用”并不一定能达到我们想要的效果。 我认为只有未被服务捕获和包装的异常才会使代理出错,但并非所有异常【参考方案4】:

通常 WCF 服务托管在 ServiceHost 中,如果 WCF-Service 失败,那么唯一的选择是终止 WCF 服务并启动一个新服务。

ServiceHost 有一个事件触发器“Faulted”,当 WCF 服务失败时会激活该触发器:

ServiceHost host = new ServiceHost(new Service.MyService());
host.Faulted += new EventHandler(host_faulted);
host.Open();

可能会得到导致故障的异常,但需要做更多的工作:

public class ErrorHandler : IErrorHandler

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    

    

    public bool HandleError(Exception error)
    
        Console.WriteLine("exception");
        return false;
    


public class ErrorServiceBehavior : IServiceBehavior

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    

    

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    

    

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    
        ErrorHandler handler = new ErrorHandler();
        foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
        
            dispatcher.ErrorHandlers.Add(handler);
        
    


ServiceHost host = new ServiceHost(new Service.MyService());
host.Faulted += new EventHandler(host_faulted);
host.Description.Behaviors.Add(new ErrorServiceBehavior());
host.Open();

学分http://www.haveyougotwoods.ca/2009/06/24/creating-a-global-error-handler-in-wcf

【讨论】:

试过了,但 HandleError 方法从未被命中,即使我命中了 Faulted 事件处理程序。【参考方案5】:

我遇到了一个问题,即 Channel 在 ReceiveTimeout 异常后仍处于故障状态。这将导致任何后续连接都无法使用该服务。

对我来说,从故障状态恢复服务的解决方法是处理通信通道的故障事件:

 channelFactory = new ChannelFactory<IService>(endpoint);
 channelFactory.Faulted += OnChannelFaulted;
 var channel = channelFactory.CreateChannel();

然后定义 OnChannelFaulted:

 void OnChannelFaulted(object sender, EventArgs e)
 
     channelFactory.Abort();
 

注意:我通过代码运行 WCF 配置,而不是在 Web.config 中使用绑定。

【讨论】:

【参考方案6】:

大约 2)...

诀窍是您应该使用“使用”并且应该始终在引发异常的代理上调用 Abort()。文章WCF Gotcha 说明了一切。

我们使用受包装服务调用的那篇文章启发的服务类。这是我项目中的示例代码:

ServiceHelper<CodeListServiceClient, CodeListService.CodeListService>.Use(
    proxy => seasonCodeBindingSource.DataSource = proxy.GetSeasonCodes(brandID);
);

这是ServiceHelper的代码,根据文章稍作修改。到目前为止,它对我们的帮助非常好。

using System;
using System.ServiceModel;

namespace Sportina.EnterpriseSystem.Client.Framework.Helpers

    public delegate void UseServiceDelegate<TServiceProxy>(TServiceProxy proxy);

    public static class ServiceHelper<TServiceClient, TServiceInterface> where TServiceClient : ClientBase<TServiceInterface>, new() where TServiceInterface : class
    
        public static void Use(UseServiceDelegate<TServiceClient> codeBlock)
        
            TServiceClient proxy = null;
            bool success = false;
            try
            
                proxy = new TServiceClient();               
                codeBlock(proxy);
                proxy.Close();
                success = true;
            
            catch (Exception ex)
            
                Common.Logger.Log.Fatal("Service error: " + ex);                                
                throw;
            
            finally
            
                if (!success && proxy != null)
                    proxy.Abort();
            
        
    

【讨论】:

“WCF Gotcha”帖子的链接已损坏,但我没有足够的代表来编辑它。这是一个很好的链接:old.iserviceoriented.com/blog/post/…。

以上是关于如何防止 WCF 服务进入故障状态?的主要内容,如果未能解决你的问题,请参考以下文章

WCF 客户端代理在被 hangfire 工作人员服务使用时陷入故障状态

如何确保您没有收到 WCF 故障状态异常?

WCF 无法用于通信,因为它处于故障状态

WCF 回调通道出现故障

WCF 通道故障状态是不是有帮助?

如何使用故障契约和 jquery ajax 处理 wcf 错误