36

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

我的问题有两个:

  1. 该服务似乎正在吞噬异常/故障,因此我无法对其进行调试。如何让服务公开异常以便我可以记录或处理它?
  2. 吞下此异常后,服务进入故障状态。如何防止服务进入故障状态?
4

6 回答 6

29

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

主页位于Channel Model Overview

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

在此处输入图像描述

于 2011-05-10T08:43:13.307 回答
21

大多数(如果不是所有)异常都可以在 WCF 跟踪(配置跟踪)中看到,最好使用服务跟踪查看器查看跟踪

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

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

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

于 2008-11-25T06:54:29.903 回答
12

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

我有点惊讶单向仍然会导致问题,但对于吞咽一般有 3 个方面:

  1. 你在抛出错误吗?还是例外?这很重要(并且应该是“错误”)
  2. 作为 hack,您可以启用调试异常消息 - 但请关闭它!!!
  3. 你在“使用”服务对象吗?我刚刚就这个确切的主题发表了博客......基本上,你的“使用”可以吞下这个例外。3个选项:

    • 不要使用“使用”
    • 子类化代理并覆盖 Dispose()
    • 把它包起来,根据博客
于 2008-11-24T22:41:14.550 回答
10

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

ServiceHost 具有在 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

于 2009-12-02T15:12:01.140 回答
7

关于 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();
            }
        }
    }
}
于 2008-11-25T07:12:15.313 回答
7

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

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

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

然后定义 OnChannelFaulted:

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

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

于 2009-09-25T22:58:27.310 回答