web-dev-qa-db-ja.com

WCFサービスが障害状態になるのを防ぐにはどうすればよいですか?

障害状態になってはならないWCFサービスがあります。例外がある場合は、ログに記録し、サービスを中断せずに続行する必要があります。サービスには一方向の操作コントラクトがあり、MSMQからメッセージを読み取ります。

私の問題は2つあります。

  1. サービスが例外/障害を飲み込んでいるように見えるため、デバッグできません。ログに記録したり処理したりできるように、例外を公開するサービスを取得するにはどうすればよいですか?
  2. この例外が飲み込まれた後、サービスは障害状態になります。サービスが障害状態になるのを防ぐにはどうすればよいですか?
34
Guy

すべてではありませんが、ほとんどの例外はWCFトレースで確認でき( トレースの構成 )、トレースは Service Trace Viewer で表示するのが最適です。

明らかに、これは実稼働環境で終日実行する必要があるものではありませんが、とにかくトラブルシューティングに役立ちます。

それとは別に、使用するSessionModeによっては、真の「ファイアアンドフォーゲット」として実行できない場合があることに注意してください。サービスをSessionMode.AllowedまたはSessionMode.Requiredに設定している場合、一方向操作は、一方向ではないかのように実行されます(これは、netTcpBindingを介してonewayを使用する場合に確認できます)。率直に言って、しかし、それがあなたが得ることができる例外のタイプを変えるのか、またはあなたがそれらをいつ得るのかを知りません。ただし、いずれにしても、リクエストをまったく送信できなかった場合は例外を取得する必要があります。知る限り、サーバー側で正常にエンキューされると、一方的に「終了」します。そのため、それまで(WCFフレームワークに関連する)例外のいくつかの場所があります(シリアライゼーション/デシリアライゼーションが思い浮かびます)。

次に、上記のトレース/トレースビューアーを使用して、このようなフレームワーク関連の例外が最もよく見られます(要求/応答フローで呼び出されたという事実により、IErrorHandlerでさえすべてが取得されません)。

19
Christian.K

障害の処理方法に関する公式ドキュメントはこちらです。

メインページは Channel Model Overview にあります

状況がどのように発生するかを示す状態図があります。

enter image description here

28

例外はプロキシにフォールトします。あなたはそれについてあまり知らない:例外を引き起こさない;-p

私は一方通行がまだ問題を引き起こしていることに少し驚いていますが、in genera lを飲み込むには、3つの側面があります:

  1. faults を投げていますか?または例外?それは重要です(そして「欠陥」でなければなりません)
  2. ハックとして、デバッグ例外メッセージを有効にすることができます-オフにしてください!!!
  3. サービスオブジェクトを「使用」していますか?私はちょうど blogged この正確な主題について...基本的に、あなたの「使用」は例外を飲み込むことができます。 3つのオプション:

    • 「使用」を使用しないでください
    • プロキシをサブクラス化し、Dispose()をオーバーライドします
    • ブログによると、それをラップ
11
Marc Gravell

通常、WCFサービスはServiceHostでホストされます。WCFサービスが失敗した場合、WCFサービスを強制終了して新しいサービスを開始するしかありません。

ServiceHostには、WCFサービスが失敗したときにアクティブになるイベントトリガー「Faulted」があります。

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

9
Rolf Kristensen

約2)...

秘Theは、「使用」を使用し、例外をスローしたプロキシで常に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();
            }
        }
    }
}
7
David Vidmar

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でバインディングを使用しています。

7
rjchicago