11

.NET 3.5、VS2008、BasicHttpBindingを使用したWCFサービス

WindowsサービスでホストされているWCFサービスがあります。アップグレードや定期的なメンテナンスなどの理由でWindowsサービスがシャットダウンした場合、WCFサービスを正常にシャットダウンする必要があります。WCFサービスには、完了するまでに最大数秒かかる可能性のあるメソッドがあり、通常のボリュームは1秒あたり2〜5回のメソッド呼び出しです。新しい呼び出しを拒否しながら、以前に呼び出したメソッドを完了できるようにWCFサービスをシャットダウンする必要があります。このようにして、約5〜10秒でクワイエット状態に到達し、Windowsサービスのシャットダウンサイクルを完了することができます。

ServiceHost.Closeを呼び出すことは正しいアプローチのように見えますが、進行中のメソッドが完了するのを待たずに、クライアント接続をすぐに閉じます。私のWCFサービスはそのメソッドを完了しますが、クライアントが既に切断されているため、応答を送信する相手がいません。これは、この質問によって提案された解決策です。

イベントのシーケンスは次のとおりです。

  1. クライアントは、VSで生成されたプロキシクラスを使用して、サービスでメソッドを呼び出します
  2. サービスがサービスメソッドの実行を開始します
  3. サービスはシャットダウンの要求を受け取ります
  4. サービスはServiceHost.Close(またはBeginClose)を呼び出します
  5. クライアントが切断され、System.ServiceModel.CommunicationExceptionを受け取ります
  6. サービスはサービスメソッドを完了します。
  7. 最終的に、サービスは(アプリケーションロジックを介して)実行する作業がなくなったことを検出し、終了します。

私が必要としているのは、クライアント接続を開いたままにして、クライアントがサービスメソッドが正常に完了したことを認識できるようにすることです。現在、接続が閉じられているだけで、サービスメソッドが正常に完了したかどうかはわかりません。WCFを使用する前は、ソケットを使用していましたが、ソケットを直接制御することでこれを行うことができました。(つまり、受信と送信を実行している間にAcceptループを停止します)

アップストリームファイアウォールがトラフィックを別のホストシステムに転送できるように、ホストHTTPポートを閉じることが重要ですが、既存の接続は開いたままにして、既存のメソッド呼び出しを完了できるようにします。

WCFでこれを実現する方法はありますか?

私が試したこと:

  1. ServiceHost.Close()-クライアントをすぐに閉じます
  2. ServiceHost.ChannelDispatchers-それぞれでListener.Close()を呼び出します-何もしないようです
  3. ServiceHost.ChannelDispatchers-それぞれでCloseInput()を呼び出します-クライアントをすぐに閉じます
  4. ServiceHost.OnClosing()をオーバーライドします-閉じることができると判断するまで閉じるを遅らせることができますが、この間は新しい接続が許可されます
  5. ここで説明する手法を使用して、エンドポイントを削除します。これはすべてを一掃します。
  6. ネットワークスニファを実行してServiceHost.Close()を監視します。ホストは接続を閉じるだけで、応答は送信されません。

ありがとう

編集:残念ながら、フィールドのクライアントはすでに展開されているため、システムがシャットダウンしているというアプリケーションレベルのアドバイザリ応答を実装できません。(私はサービスのみを制御し、クライアントは制御しません)

編集:Redgate Reflectorを使用して、MicrosoftのServiceHost.Closeの実装を確認しました。残念ながら、internalコードがアクセスできないいくつかのヘルパークラスを呼び出します。

編集:探していた完全な解決策は見つかりませんでしたが、サービスメソッドを入力する前にIMessageDispatchInspectorを使用してリクエストを拒否するというBenjaminの提案が最も近かったです。

4

8 に答える 8

6

推測:

実行時に(エンドポイントから)バインディングを取得し、それをBasicHttpBindingにキャストして、そこでプロパティを(再)定義しようとしましたか?

私からの最良の推測:

  • OpenTimeout
  • MaxReceivedMessageSize
  • ReaderQuotas

これらは、ドキュメントに従って実行時に設定でき、目的の動作(新しいクライアントのブロック)を可能にするようです。ただし、これは「アップストリームファイアウォール/ロードバランサーが再ルーティングする必要がある」部分には役立ちません。

最後の推測:エンドポイントのアドレスをオンデマンドでローカルホストアドレスに再定義できますか(ドキュメントには「はい」と記載されていますが、結果はわかりません)?とにかくすべてのクライアントを強制終了しない場合、これはファイアウォールホストの「ポートクローズ」としても機能する可能性があります。

編集:上記の提案と限定されたテストで遊んでいる間、私は今のところ有望に見えるメッセージインスペクター/動作の組み合わせで遊んで始めました:

public class WCFFilter : IServiceBehavior, IDispatchMessageInspector {
    private readonly object blockLock = new object();
    private bool blockCalls = false;

    public bool BlockRequests {
        get {
            lock (blockLock) {
                return blockCalls;
            }
        }
        set {
            lock (blockLock) {
                blockCalls = !blockCalls;
            }   
        }

    }

    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) {
        foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers) {
            foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) {
                endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
            }
        } 
    }

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {
        lock (blockLock) {
            if (blockCalls)
                request.Close();
        }
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState) {           
    }
}

安っぽいロックの使用法などは忘れてください。ただし、次のような非常に単純なWCFテスト(Thread.Sleepを含む乱数を返す)でこれを使用します。

var sh = new ServiceHost(new WCFTestService(), baseAdresses);

var filter = new WCFFilter();
sh.Description.Behaviors.Add(filter);

その後、BlockRequestsプロパティを反転すると、次の動作が発生します(これも、もちろん非常に単純化された例ですが、とにかくうまくいくことを願っています)。

//3つのスレッドを生成します番号を
要求します..番号を
要求します..番号を要求します..//1
つの受信要求のサーバー側ログ
番号の受信要求。
//メインループは「すべてをブロックする」boolを反転しますこれからは
アクセスをブロックします。
//その後、さらに3つのクライアント、適切な方法
で番号をリクエストします。番号をリクエストし ます。番号
をリクエストします。// 最初のリクエスト(サーバー側のログを使用、上記を参照)は正常に完了します 受信済み1569129641 //他のすべてのメッセージは作成されませんそれはまだサーバーに送信 され、ブロック後に生成されたクライアント要求のエラーエラーで終了します。 ブロック後に生成されたクライアント要求のエラー。






ブロック後に生成されたクライアント要求のエラー。
ブロック前のクライアント要求でエラーが発生しました。
ブロック前のクライアント要求でエラーが発生しました。

于 2009-12-21T14:21:20.550 に答える
2

アップストリームファイアウォール用のAPIはありますか?アプリケーションでこれを行う方法は、ロードバランサーレベルで着信する新しいリクエストを停止し、すべてのリクエストの処理が終了したら、サーバーとサービスを再起動できるようにすることです。

于 2009-12-21T13:12:31.993 に答える
1

私の提案は、サービスが「停止状態」になったときにEventHandlerを設定し、OnStopメソッドを使用することです。サービスが停止状態になることを示すEventHandlerを設定します。

通常のサービスループは、このイベントが設定されているかどうかを確認する必要があります。設定されている場合は、呼び出し元のクライアントに「サービスが停止しているメッセージ」を返し、通常のルーチンに入らないようにします。

アクティブなプロセスがまだ実行されている間に、OnStopメソッドがWCFホスト(ServiceHost.Close)の強制終了に進む前に、プロセスを終了させます。

もう1つの方法は、独自の参照カウンターを実装して、アクティブな呼び出しを追跡することです。次に、参照カウンターがゼロに達したときにサービスホストを停止できる時期を確認し、上記のチェックを実装することで、停止イベントが開始された時期を確認します。

お役に立てれば。

于 2009-12-21T14:18:35.113 に答える
0

この WCF サービスは何らかの方法でユーザーを認証しますか? 「握手」方法はありますか?

于 2009-12-21T12:57:45.093 に答える
0

Matthew Steeples からの回答に加えて。

F5 などの最も重要なロード バランサーには、ノードが稼働しているかどうかを識別するメカニズムがあります。あなたの場合、特定のポートが開いているかどうかを確認しているようです。ただし、別の方法を簡単に構成できます。

したがって、たとえば 2 つのサービスを公開できます。リクエストを処理する実際のサービスと、「ハートビート」のような監視サービスです。メンテナンス モードに移行するときは、最初に監視サービスをオフラインにすることができます。これにより、ノードから負荷が取り除かれ、すべてのリクエストの処理が終了した後に実際のサービスのみがシャットダウンされます。少し奇妙に聞こえますが、シナリオで役立つかもしれません...

于 2009-12-22T20:35:21.453 に答える
0

私はこれを自分で実装していないので、YMMV ですが、あなたがしようとしているのは、サービスを完全に停止する前にサービスを一時停止することだと思います。一時停止は、既存の要求を完了している間、新しい接続を拒否するために使用できます。

.NET では、サービスを一時停止する方法はServiceControllerを使用することです。

于 2009-12-17T15:38:29.880 に答える
0

実行中のすべてのリクエストを追跡するヘルパー クラスを使用して独自の実装を作成する必要があるかもしれないと思います。その後、シャットダウンがリクエストされたときに、何かがまだ実行されているかどうかを確認し、それに基づいてシャットダウンを遅らせることができます... (タイマーかな?)

それ以上の着信リクエストをブロックするかどうかわからない...シャットダウンがリクエストされたかどうかをアプリケーションに伝えるグローバル変数が必要なので、それ以上のリクエストを拒否できます...

これがお役に立てば幸いです。

于 2009-12-21T13:21:18.173 に答える
0

多分あなたは

ServiceBehaviorAttribute および OperationBehavior 属性。MSDNでこれを確認してください

于 2009-12-21T22:23:13.523 に答える