2011-01-07 3 views
0

私は単純なテストケースのWCFサービスを持っています。考えられるのは、あるプロセスが別のプロセスをコールしてコールバックを登録し、もう1つがイベント発生時に元の呼び出し元を呼び出すということです。 WCFのネイティブコールバックが正しく動作しない、またはこのアプリケーションでは、それを示唆することはありません。私は動作を示す非常に単純なテストプログラムを持っています。 10回の試行ごとに、しばらくの間(一時停止期間)、それ以降の回復が一時停止します。これは、開いている/閉じる/何であれ、チャネルイベントを発生させることなく回復します。私は明らかに何かが欠けする必要があるが、何...WCFは10回の呼び出しごとに黙って威嚇的な方法で失敗します

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.ServiceModel; 
using System.Threading; 

namespace WCFTest 
{ 
[ServiceContract(Namespace = "http://WCF.WTF")] 
public interface IServerEvents 
{ 
    [OperationContract(IsOneWay = true)] 
    void Heartbeat(); 
} 

[ServiceContract(Namespace = "http://WCF.WTF")] 
public interface ICallbackEvents 
{ 
    [OperationContract(IsOneWay = true)] 
    void HeartbeatAck(); 
} 

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode =  InstanceContextMode.Single)] 
[CallbackBehavior(IncludeExceptionDetailInFaults = true)] 
class Program : IServerEvents, ICallbackEvents 
{ 
    static AutoResetEvent CalledBack = new AutoResetEvent(false); 
    static ChannelFactory<IServerEvents> ServerChannelFactory; 
    static ChannelFactory<ICallbackEvents> ClientChannelFactory; 
    static ServiceHost ServerHost; 
    static ServiceHost ClientHost; 

    static int Timeout = 5; 

    private Program() 
    { 
    } 

    static void Main(string[] args) 
    { 
    NetTcpBinding binding = new NetTcpBinding(SecurityMode.None) 
    { 
    OpenTimeout = new TimeSpan(0, 0, Timeout), 
    SendTimeout = new TimeSpan(0, 0, Timeout), 
    ReceiveTimeout = new TimeSpan(0, 0, Timeout), 
    MaxConnections = 50, 
    ListenBacklog = 50  
    }; 
    Uri serverUri = new Uri("net.tcp://localhost:3123/WTF"); 
    ServerChannelFactory = new ChannelFactory<IServerEvents>(binding, new  EndpointAddress(serverUri)); 
    ClientChannelFactory = new ChannelFactory<ICallbackEvents>(binding, new  EndpointAddress("net.tcp://localhost:3123/WTF/Client")); 
    ServerChannelFactory.Closing += new EventHandler((s,x) =>  Console.WriteLine("SClosing")); 
    ServerChannelFactory.Opening += new EventHandler((s, x) => Console.WriteLine("SOpening")); 
    ServerChannelFactory.Faulted += new EventHandler((s,x)=> Console.WriteLine("SFaulted")); 
    ClientChannelFactory.Closing += new EventHandler((s, x) => Console.WriteLine("CClosing")); 
    ClientChannelFactory.Opening += new EventHandler((s, x) => Console.WriteLine("COpening")); 
    ClientChannelFactory.Faulted += new EventHandler((s, x) => Console.WriteLine("CFaulted")); 

    ServerHost = StartServer("/Server", new Program(), typeof(IServerEvents)); 
    ClientHost = StartServer("/Client", new Program(), typeof(ICallbackEvents)); 

    while (true) 
    { 
    Thread.Sleep(100); 
    try 
    { 
    new Program().Heartbeat(); 
    if (!CalledBack.WaitOne(2500, true)) 
    { 
     throw new TimeoutException("Epic fail."); 
    } 
    } 
    catch (Exception x) 
    { 
    Console.WriteLine("Failed heartbeat.\n{0}", x); 
    } 
    } 
    } 

    public void Heartbeat() 
    { 
    Console.Write("."); 
    Console.Out.Flush(); 
    try 
    { 
    ClientChannelFactory.CreateChannel().HeartbeatAck(); 
    } 
    catch (Exception x) 
    { 
    Console.WriteLine("Couldn't ACK heartbeat.\n{0}", x); 
    } 
    } 

    public void HeartbeatAck() 
    { 
    Console.Write("!"); 
    Console.Out.Flush(); 
    CalledBack.Set(); 
    } 

    private static ServiceHost StartServer<T>(string fragment, T remoteObject, Type interfaceType) 
    { 
    ServiceHost retHost = null; 
    using (AutoResetEvent revent = new AutoResetEvent(false)) 
    { 
bool hostOk = false; 
// The service host has to be started on a non-sync-context thread or bad things (tm) will happen. 
ThreadPool.QueueUserWorkItem((oo) => 
{ 
try 
{ 
    retHost = new ServiceHost(remoteObject, new Uri("net.tcp://localhost:3123/WTF")); 
    var binding = new NetTcpBinding(SecurityMode.None) 
     { 
     OpenTimeout = new TimeSpan(0, 0, Timeout), 
     SendTimeout = new TimeSpan(0, 0, Timeout), 
     ReceiveTimeout = new TimeSpan(0, 0, Timeout), 
     MaxConnections = 50, 
     ListenBacklog = 50   
     }; 
    retHost.AddServiceEndpoint(interfaceType, binding, fragment); 
     retHost.Open(); 
     hostOk = true; 
    } 
    catch (Exception xc) 
    { 
     Console.WriteLine("Couldn't start WCF Service Host!\n{0}", xc); 
    } 
    finally 
    { 
     try { revent.Set(); } 
     catch { } 
    } 
    }); 
    revent.WaitOne(5000, true); 
    return hostOk ? retHost : null; 
    } 
    } 
} 
} 
+2

これをすべて読んだ人には別の点があります。 –

+1

ところで、この種のことは、IDisposableを実装しているオブジェクトでDisposeを呼び出さないことを意味します。 –

+0

質問:あなたはなぜあなたのサービスをシングルトンにするのですか?これはちょうどトラブルを必要とします....ベストプラクティスは、通話サービスのアクティベーションを行うことです - プログラムするのがずっと簡単です! –

答えて

0

ここでの根本的な問題は、ドキュメントが良くなく、実行時の報告が悪いことです。確かめるのは容易ではありませんでしたが、10のアクティブな接続のうちのいくつかの制限(MaxConnections/ListenBackLogではなく)があるようです。 Disposeはいつでも呼び出されるので、ChannelFactoryはいつでも接続を解放します。結果として、10個の接続が使い果たされ、Disposeが実行されるまで、(ソケット要求をしている間、指定されたタイムアウトの対象となる「実際のWCF」アクティビティを実行している間ではなく)一時停止します。つまり、オープンチャネルファクトリをWCFに「長期間」保管することはできません。特に、工場を複数回開いたり閉じたりすることができないため、悪いと思います。つまり、Bindings/EndpointAddressesを保存するだけです。

+1

Disposeの使用は非常によく文書化されています。 Disposeに電話する必要がないと決めました。これはリソースをすぐに解放することを意味します。何が起こったのかというと、リソースが十分に素早く解放されずに使い果たされたということです。サプライズ、驚き! –

+0

処分が必要なオブジェクトはIDisposableではないので、まったく問題ではありません。これは問題全体の一種です。 –

0

[これが答えではありませんが、コメントとして収めるには長すぎる。]

テストケースは、元のコードとは異なり。問題がどこにあるのかが本当にわからない場合は、コードベースを変更して結果を歪めるようなことはありません。

代わりに、2つのユニットを別々にテストできるように、クライアントとサーバーをモックアウトしてください。 WcfTestClientはクライアントを非常にうまく処理します。

サービスの性質上、プライマリレイヤではロジックが0に近いようにして、実装を再利用可能なDLL(MVCとn層の結合)にバウンスすることをお勧めします。これにより、デバッグの目的でWCFサービス(コンソールアプリケーションのような)上に新しいフロントエンドを作成するのが非常に簡単になります。

+0

これは本当に正直に私に意味をなさない。テストケースもまったく同じ方法で失敗します。したがって、このテストケースを修正しても本物は修正されない可能性がありますが、それはかなり恐ろしいことです。また、自分自身のようなスマートな人たちが実際にその事例を見て実行することができるということは、私が確信しているリスクです。 –

+0

あなたのテストケースが同じように失敗するのは良いことです。はい、それはテストを修正することは、プログラムに変換されるという自信がたくさんあります。しかし、この例からトリムできる脂肪はまだたくさんあります。私は助けたいと思っていますが、他の人たちが暗示しているように、実際の問題にまで縮小される可能性のある巨大なコードを見たくはありません。上で説明したような単純な概念を使用することで、これを自分で行うことができ、同時に保守性を向上させることができます。 –

+0

それは本当に削減する可能性はあまりありません。確かに、私は例外処理と問題の印刷の束を取り出すことができますが、それは一種のポイントです。上記のコンセプトは、このMOREコードを掘り下げて(別のクラス、もっと多くのDLL、コンソールアプリケーションではない)... ...私はちょうど最終的に回避策を見つけたり、TCPサーバーを作成することになります。 –

関連する問題