WCF代理包装器 - 离线缓冲数据,重新连接并在线重新发送一次

时间:2015-09-14 19:12:28

标签: c# wcf error-handling fault

我已经编写了一个WCF类的包装器,它将缓冲发送数据,并在重新连接后发送一次。我有以下问题:

  1. 我是否需要对所有渠道故障事件进行控制,或者ChannelFactory.Faulted是否足够?

  2. 这是在很多深夜之后写的,并且会欣赏一双新鲜的眼睛,任何人都可以看到下面有什么令人震惊的声音吗?

  3. 任何推荐阅读实现一般重新连接和最佳实践(我已经看过Polly库,但无法看到它将如何在后台线程中执行重新连接)所以我不重新发明轮子?任何通用框架,以实现我试图写的

  4. 请参阅下面的代码:

    public class ClientProxyWrapper
    {
        private readonly Action<Dictionary<string, string>> _incomingCallCallback;
        private ScreenPopClient _proxy;
        private volatile bool _isConnecting;
        private volatile bool _isReady;
        private readonly object _lock = new object();
        private readonly InstanceContext _context;
        private TreadSafeStack<Dictionary<string,string> _offlineValues = new TreadSafeStack<Dictionary<string,string>();
    
        public ClientProxyWrapper(Action<Dictionary<string,string>> incomingCallCallback)
        {
            _isReady = false;
            _incomingCallCallback = incomingCallCallback;
            _context = new InstanceContext(this);
            StartConnectTask(0);
        }
    
        void CreateNewProxy()
        {
            _proxy = new ScreenPopClient(_context);
            _proxy.ChannelFactory.Faulted += OnChannelFault;
            _proxy.InnerChannel.Faulted += OnChannelFault;            
            _proxy.InnerDuplexChannel.Faulted += OnChannelFault;            
        }
    
        void StartConnectTask(int startDelay)
        {
            lock (_lock)
            {                
                if (_isConnecting) return; // we are already connecting
                _isConnecting = true;
            }
    
            Task.Run(() =>
            {
                while (true)
                {
                    Thread.Sleep(startDelay);
                    try
                    {
                        CreateNewProxy();
                        _proxy.Login(Environment.UserName);
    
                        Dictionary<string,string> toSend;
                        while(_offlineValues.Get(out toSend))
                        {
                            _proxy.Update(toSend);
                        }
                        _isConnecting = false;
                        _isReady = true;
                        return;
                    }
                    catch (Exception)
                    {
                    }
                }
            });
        }
    
        void OnChannelFault(object sender, EventArgs e)
        {
            ((ICommunicationObject)sender).Abort();
            // reconnect...
            StartConnectTask(5000);
        }
    
        void Update(Dictionary<string,string> values)
        {
            if(_isReady)
            {
                // put values into a thread safe queue, to be sent once we come back online
                _offlineValues.Add(values);
            }
            else
            {
                _proxy.Update(values);
            }
        }
    }
    

2 个答案:

答案 0 :(得分:2)

只看代码 - 一般印象:

让Task.Run运行同步方法有点尴尬 - 而且一般认为不能很好地利用任务。

看起来你有失去工作的能力。例如,由于连接问题或其他原因,您如何处理尚未发送的数据?我认为有几个开放边缘案例。例如,您可以从多个线程调用Update,而不会丢失任何内容。如果你有_offlineValues并且你正在关闭,会发生什么?

我不知道TreadSafeStack是什么。那只是样品 - 是吗?你在考虑ConcurrentQueue吗?

演员之间的关系似乎很尴尬。通常,我认为您有一个处理器将通信与工作排队方法分开,该工作排队方法只是将工作提交给处理器。处理器将检测代理的状态并根据需要重新连接。

可以轻松地将工作排入线程安全。您可能不想在排队时等待服务。最好将长时间运行的发送与快速运行的提交分开。

我有类似的东西 - 它只是外壳 - 我剥离了所有使我的东西与你的东西不同的噪音 - 但也许你可以得到我所说的从它(我的Enqueue将类似于你的更新)。

class Sender
{
  /// <summary>Synchronization primitive</summary>
  private static object sync = new object( );

  /// <summary>Processor wait this long before checking for work (if not nudged)</summary>
  private const int waitTimeOutMilliseconds = 5000;

  /// <summary>What the processor waits on to hear that there are things to do</summary>
  private ManualResetEvent nudge = new ManualResetEvent( false );

  /// <summary>The processor sets this when it's been signaled to stop and message processing is complete</summary>
  private ManualResetEvent done = new ManualResetEvent( false );

  /// <summary>A flag that will be set when the host wants to terminate the message queue processor</summary>
  private bool shutDown = true;

  /// <summary>A queue of messages that need to be sent</summary>
  private Queue<string> queue = new Queue<string>( );


  /// <summary>Puts a message in the queue</summary>
  public void Enqueue( string message )
  {
    lock ( sync )
    {
      if ( shutDown ) throw new InvalidOperationException( "Shutting down - not accepting messages" );
      queue.Enqueue( message );
      nudge.Set( );
    }
  }

  /// <summary>Shuts down without waiting</summary>
  public void Stop( )
  {
    lock ( sync )
    {
      shutDown = true;
      nudge.Set( );
    }
  }

  /// <summary>Starts queue processing</summary>
  public void Start( )
  {
    if ( WaitForShutdown( 5000 ) )
    {
      lock ( sync )
      {
        shutDown = false;
        done.Reset( );
        nudge.Reset( );
        ThreadPool.QueueUserWorkItem( Processor );
      }
    }
    else
    {
      throw new InvalidOperationException( "Couldn't start - that's bad!" );
    }
  }

  /// <summary>Stops accepting messages on the queue, triggers shutdown and waits for the worker thread to complete.</summary>
  /// <param name="millisecondsToWait"></param>
  /// <returns>True if the thread stops in the time specified, false otherwise</returns>
  private bool WaitForShutdown( int millisecondsToWait )
  {
    Stop( );
    lock ( sync )
    {
      if ( shutDown ) return true;
    }
    return done.WaitOne( millisecondsToWait );
  }


  /// <summary>Worker thread method that writes the message</summary>
  private void Processor( object state )
  {

    var processList = new List<string>( );  //--> work we'll take out of the queue
    var cancel = false;  //--> a local representation of shutdown, we'll obtain while under a lock

    while ( true )
    {
      nudge.WaitOne( waitTimeOutMilliseconds );

      lock ( sync )
      {
        cancel = shutDown;
        while ( queue.Any( ) )
        {
          processList.Add( queue.Dequeue( ) );
        }
        nudge.Reset( );
      }

      foreach ( var message in processList )
      {
        try
        {
          // send to service...
        }
        catch ( Exception ex )
        {
          // reconnect or do whatever is appropriate to handle issues...
        }
        if ( cancel ) break;
      }

      processList.Clear( );

      if ( cancel )
      {
        done.Set( );
        return;
      }
    }
  }
}

答案 1 :(得分:1)

如果ClientProxyWrapper正在运行的进程意外终止,那么所有邮件会发生什么?

您的邮件没有持久性,因此无法保证它们会被送达。如果您使用像MSMQ这样的排队系统来存储消息然后从那里处理它们会更好。

消息队列可以是跨国的,并且减少丢失消息的可能性。