1

システムのさまざまなコンポーネントすべてからログ ファイルを取得し、クライアントに送信する非同期 WCF サービス操作があります。コンポーネントの 1 つが正常に動作していない場合、これには時間がかかる可能性があるため、この機能がタイムアウトにならないことは望ましいことですが、クライアントがハングすることもありません。

非同期 WCF サービスについての私の理解では、クライアントがサーバーに何かを要求すると、サーバーはすぐに次のようなメッセージで応答します。終わりました。」次に、サーバーが新しいスレッドを起動して大部分の作業を実行している間、クライアントが他の要求を行うために接続が解放されます。サーバーが終了すると、結果を含むメッセージをクライアントに送り返します。このため、サーバーとクライアント間の接続は自由であり、サーバーの所要時間に関係なく、接続がタイムアウトすることはありません。これは正しいです?

その場合、当社のサービスは期待どおりに機能していません。サービスをテストすると、1 分もかからない限り、期待どおりに機能します。ただし、それよりも長くかかるように強制すると、クライアントは TimeoutException をスローします。サービスは非同期なので、タイムアウトすることはありませんか? もしそうなら、私は何が欠けていますか?

このページをガイドとして使用して非同期サービスを作成しました: http://code.msdn.microsoft.com/windowsdesktop/How-to-Implement-a-WCF-2090bec8

これが私のコードです。これはサービス契約です:

[ServiceContract(CallbackContract = typeof(IInformationServiceCallBack), SessionMode = SessionMode.Required)]
public interface IInformationService
{
    //snip...

    [OperationContract(AsyncPattern=true)]
    [FaultContract(typeof(LogFileFault))]
    IAsyncResult BeginGetLogFiles(LogFileRequest[] logfileRequests,
        AsyncCallback callback, object state);

    LogFile[] EndGetLogFiles(IAsyncResult result);

    //snip...
}

これはサービスの実装です:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession, UseSynchronizationContext=false)]
public class InformationServiceImpl : IInformationService, IDisposable
{
    //snip...

    public IAsyncResult BeginGetLogFiles(LogFileRequest[] logfileRequests,
        AsyncCallback callback, object state)
    {
        var task = Task<LogFile[]>.Factory.StartNew((x) =>
            {
                return GetLogFilesHelper(logfileRequests);
            }, state);

        return task.ContinueWith(res => callback(task));
    }

    public LogFile[] EndGetLogFiles(IAsyncResult result)
    {
        var castResult = result as Task<LogFile[]>;
        return castResult.Result;
    }

    private LogFile[] GetLogFilesHelper(LogFileRequest[] logfileRequests)
    {
        //Long-running method that gets the log files
    }

    //snip...
}

クライアント側のコードは次のとおりです。

public class InformationServiceConnection : WcfDurableConnection<IInformationService> //WcfDurableConnection is one of our internal classes
{
    //snip...

    public void GetServiceLogFiles(Action<LogFile[], WcfCommandResult> callback)
    {
        var logfileRequests = new LogFileRequest[]
        { 
            new LogFileRequest(/* snip */),
            new LogFileRequest(/* snip */),
            new LogFileRequest(/* snip */),
            new LogFileRequest(/* snip */)
        };

        ExecuteTask(x =>
            {
                LogFile[] logfile = null;
                WcfCommandResult wcfResult = null;

                var asyncCallback = new AsyncCallback((result) =>
                {
                    logfile = Channel.EndGetLogFiles(result);
                    callback(logfile, wcfResult);
                });

                wcfResult = RunCommand(y =>
                {
                    Channel.BeginGetLogFiles(logfileRequests, asyncCallback, null);
                }, x);
            });
    }

    /* ExecuteTask and RunCommand are both methods that take care of
     * multithreading issues for us. I included their code below in
     * case they make a difference, but the code I'm most interested
     * in is the GetServiceLogFiles method above. */

    //snip...
    protected CancellationTokenSource ExecuteTask(Action<CancellationToken> action)
    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();

        ManualResetEvent lastTask;
        ManualResetEvent thisTask;
        lock (_objectLock)
        {
            lastTask = _syncTask;
            thisTask = new ManualResetEvent(false);
            _syncTask = thisTask;
        }

        tokenSource.Token.Register(x => ((ManualResetEvent)x).Set(), thisTask);

        var task = Task.Factory.StartNew((x) =>
        {
            try
            {
                lastTask.WaitOne();
                action((CancellationToken)x);
            }
            catch (Exception e)
            {
                LogUtility.Error(e);
            }
            finally
            {
                thisTask.Set();
            }
        }, tokenSource.Token, tokenSource.Token).HandleExceptions();

        return tokenSource;
    }

    //snip...

    protected WcfCommandResult RunCommand(Action<CancellationToken> action, CancellationToken token, bool isRestarting = false)
    {
        return RunCommand(x => { action(x); return true; }, token, isRestarting);
    }

    protected WcfCommandResult RunCommand(Func<CancellationToken, bool> action, CancellationToken token, bool isRestarting = false)
    {
        WcfCommandResult result = new WcfCommandResult();

        lock (_reconnectionLock)
        {
            if (_reconnecting && !isRestarting)
            {
                result.Completed = false;
                return result;
            }
        }


        lock (_channelLock)
        {
            if (Channel == null && !_closing)
            {
                token.ThrowIfCancellationRequested();
                Channel = GetNewChannel();
                var iChannel = (IClientChannel)Channel;
                var initResult = Initialize(token, false);

                if (initResult.Completed)
                {
                    Connected = true;
                    LogUtility.Info(string.Format("Connected to {0} at {1}", ServiceName, iChannel.RemoteAddress));
                }
                else
                    LogUtility.Info(string.Format("Failed to connect to {0} at {1}", ServiceName, iChannel.RemoteAddress));
            }
        }

        try
        {
            var channel = Channel;
            token.ThrowIfCancellationRequested();
            if (channel != null)
                result.Completed = action(token);
        }
        catch (FaultException e)
        {
            result.Exception = e;
            result.Detail = e.GetDetail<DurableFault>();
            LogUtility.Error(result.Exception);
        }
        catch (CommunicationException e)
        {
            Connected = false;
            result.Exception = e;
            IClientChannel channel = ((IClientChannel)Channel);
            if (channel != null)
                channel.Abort();
            Channel = null;
            if (!_reconnecting)
                LogUtility.Error(result.Exception);
        }
        catch (TimeoutException e)
        {
            Connected = false;
            result.Exception = e;
            IClientChannel channel = ((IClientChannel)Channel);
            if (channel != null)
                channel.Abort();
            Channel = null;
            if (!_reconnecting)
                LogUtility.Error(result.Exception);
        }
        catch (NullReferenceException e)
        {
            Connected = false;
            result.Exception = e;
            IClientChannel channel = ((IClientChannel)Channel);
            if (channel != null)
                channel.Abort();
            Channel = null;
            if (!_reconnecting)
                LogUtility.WriteException("Channel is null, it has either been disposed or not setup, call BeginSetupUser to create a new channel", e);
        }
        catch (ObjectDisposedException e)
        {
            Connected = false;
            result.Exception = e;
            IClientChannel channel = ((IClientChannel)Channel);
            if (channel != null)
                channel.Abort();
            Channel = null;
            if (!_reconnecting)
                LogUtility.Error(result.Exception);
        }
        catch (InvalidOperationException e)
        {
            Connected = false;
            result.Exception = e;
            IClientChannel channel = ((IClientChannel)Channel);
            if (channel != null)
                channel.Abort();
            Channel = null;
            if (!_reconnecting)
                LogUtility.Error(result.Exception);
        }

        return result;
    }

    //snip...
}
4

1 に答える 1

2

非同期呼び出しの場合でも、構成ファイルにタイムアウトが設定されています。応答に時間がかかる場合は、おそらく値を大きくする必要があります。デフォルトは1分だと思います。Visual Studio で、[ツール] -> [WCF サービス構成エディター] に移動して、値を簡単に変更します。

これは、構成がどのように見えるかを確認したい場合にも役立ちます: WCF サービスでタイムアウト値を増やす

その構成ファイルまたはコードビハインドで設定できます。

于 2012-07-17T18:40:34.087 に答える