1

ログ ファイルの分析 サービス呼び出しの約 1% が、Silverlight クライアント側で TimeoutException で終了していることに気付きました。サービス (wcf) は非常に単純で、長い計算を実行しません。ログによると、サービスへのすべての呼び出しは常に 1 秒未満で処理されるため (クライアントで TimeoutException が発生した場合でも!)、サーバーのタイムアウトではありません。

では、何が問題なのですか?構成またはネットワークの問題でしょうか? どうすれば回避できますか?この問題のローカライズに役立つ追加のログ情報は何ですか?

私が考えた唯一の回避策は、タイムアウト後にサービス呼び出しを再試行することです。

この問題について何か助けていただければ幸いです。

更新:起動時に、アプリケーションは 17 のサービス コールを実行し、そのうち 12 を同時に実行します (失敗の原因でしょうか?)。

更新: WCF ログには、この問題に関する有用な情報が含まれていません。一部のサービスコールがサーバー側に届かないようです。

4

3 に答える 3

6

問題は、Internet Explorer 7/6 での単一サーバーへの同時接続の最大数にあります。2つだけです!http://msdn.microsoft.com/en-us/library/cc304129(VS.85).aspx

たとえば、3 つの同時サービス呼び出しがある場合、そのうち 2 つがすぐにサーバーに送信されますが、3 つ目はキューで待機します。sendTimeoutまた、リクエストがキューにあるときは、送信タイマー ( に対応) が実行されます。最初の 2 つのサービス要求が長時間実行されると、サーバーに送信されていないにもかかわらず、3 番目のサービス要求で TimeoutException が生成されます (サーバー側ではこの要求に関する情報が表示されず、Fiddler でキャッチできません) ...)。

より現実的な状況では、約 12 の同時呼び出しがあり、デフォルトで 1 分間の送信タイムアウトがあり、サービス呼び出しが平均で 10 秒以上処理され、最後の 2 つの呼び出しで簡単にタイムアウト例外が発生する場合 (12 / 2 * 10 秒 = 60 秒) ) 彼らは他のすべてを待つからです。

解決策は次のとおりです。

  1. 同時サービス呼び出しの数を最小限に抑えます。
  2. クライアントsendTimeout構成の値を増やします。
  3. 重要なサービスの自動再試行機能を実装します。
  4. それらを管理するためにリクエストのキューを実装します。

私の場合、1 ~ 3 のことを実行しましたが、それで十分でした。

自動再試行機能の私の実装は次のとおりです。

public static class ClientBaseExtender
{
    /// <summary>
    /// Tries to execute async service call. If <see cref="TimeoutException"/> occured retries again.
    /// </summary>
    /// <typeparam name="TChannel">ServiceClient class.</typeparam>
    /// <typeparam name="TArgs">Type of service client method return argument.</typeparam>
    /// <param name="client">ServiceClient instance.</param>
    /// <param name="tryExecute">Delegate that execute starting of service call.</param>
    /// <param name="onCompletedSubcribe">Delegate that subcribes an event handler to the OnCompleted event of the service client method.</param>
    /// <param name="onCompleted">Delegate that executes when service call is succeeded.</param>
    /// <param name="onError">Delegate that executes when service call fails.</param>
    /// <param name="maxAttempts">Maximum attempts to execute service call before error if <see cref="TimeoutException"/> occured (by default 5).</param>
    public static void ExecuteAsyncRepeatedly<TChannel, TArgs>(this ClientBase<TChannel> client, Action tryExecute,
                                                               Action<EventHandler<TArgs>> onCompletedSubcribe, EventHandler<TArgs> onCompleted,
                                                               EventHandler<TArgs> onError, int maxAttempts)
        where TChannel : class
        where TArgs : AsyncCompletedEventArgs
    {
        int attempts = 0;
        var serviceName = client.GetType().Name;

        onCompletedSubcribe((s, e) =>
                                {
                                    if (e.Error == null) // Everything is OK
                                    {
                                        if (onCompleted != null)
                                            onCompleted(s, e);

                                        ((ICommunicationObject)client).Close();
                                        Debug.WriteLine("[{1}] Service '{0}' closed.", serviceName, DateTime.Now);
                                    }
                                    else if (e.Error is TimeoutException)
                                    {
                                        attempts++;

                                        if (attempts >= maxAttempts) // Final timeout after n attempts
                                        {
                                            Debug.WriteLine("[{2}], Final Timeout occured in '{0}' service after {1} attempts.", serviceName, attempts, DateTime.Now);

                                            if (onError != null)
                                                onError(s, e);
                                            client.Abort();

                                            Debug.WriteLine("[{1}] Service '{0}' aborted.", serviceName, DateTime.Now);
                                            return;
                                        }

                                        // Local timeout
                                        Debug.WriteLine("[{2}] Timeout occured in '{0}' service (attempt #{1}).", serviceName, attempts, DateTime.Now);

                                        Debug.WriteLine("[{2}] Attempt #{0} to execute call to '{1}' service.", attempts + 1, serviceName, DateTime.Now);
                                        tryExecute(); // Try again.
                                    }
                                    else
                                    {
                                        if (onError != null)
                                            onError(s, e);
                                        client.Abort();
                                        Debug.WriteLine("[{1}] Service '{0}' aborted.", serviceName, DateTime.Now);
                                    }
                                });

        Debug.WriteLine("[{2}] Attempt #{0} to execute call to '{1}' service.", attempts + 1, serviceName, DateTime.Now);
        tryExecute(); // First attempt to execute
    }
}

そして、ここに使用法があります:

var client = new MyServiceClient();
client.ExecuteAsyncRepeatedly(() => client.MyOperationAsync(...),
    (EventHandler<MyOperationCompletedEventArgs> handler) => client.MyOperationCompleted += handler,
    (s, e) => // OnCompleted
        {
            Do(e.Result);
        },
    (s, e) => // OnError
        {
            HandleError(e.Error);
        }
);

これが役立つことを願っています。

于 2009-06-03T22:37:37.450 に答える
0

うーん...リクエスト/レスポンスが64K以上、またはシリアル化されたオブジェクトが多すぎる可能性はありますか?

コンソール アプリケーションを使用してサーバーにヒットするシミュレーションを作成してみてください (ネットワークかどうかを確認するためだけに、SL...)。

于 2009-05-25T12:51:23.830 に答える