C# WCF代理包装器-脱机缓冲数据,重新连接并再次联机发送

C# WCF代理包装器-脱机缓冲数据,重新连接并再次联机发送,c#,wcf,error-handling,fault,C#,Wcf,Error Handling,Fault,我已经在WCF类周围编写了一个包装器,它将缓冲发送数据,并在重新连接后发送。我有以下问题: 我是否需要记录所有通道故障事件,或者ChannelFactory.faulted是否足够故障 这封信是贝尼在许多深夜之后写的,希望能有一双新的眼睛,有人能看到下面令人震惊的东西吗 关于实现常规重新连接和最佳实践的任何推荐阅读资料(我看过Polly库,但看不到如何在后台线程中执行重新连接),所以我不重新发明轮子?任何通用的框架来实现我试图写的东西 请参阅下面我的代码: public class Client

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

  • 我是否需要记录所有通道故障事件,或者ChannelFactory.faulted是否足够故障

  • 这封信是贝尼在许多深夜之后写的,希望能有一双新的眼睛,有人能看到下面令人震惊的东西吗

  • 关于实现常规重新连接和最佳实践的任何推荐阅读资料(我看过Polly库,但看不到如何在后台线程中执行重新连接),所以我不重新发明轮子?任何通用的框架来实现我试图写的东西

  • 请参阅下面我的代码:

    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);
            }
        }
    }
    
    公共类ClientProxyWrapper
    {
    私有只读操作_incomingCallCallCallback;
    私有ScreenPopClient_代理;
    私有易失性网络断开连接;
    私人易变资产已准备就绪;
    私有只读对象_lock=新对象();
    私有只读InstanceContext_上下文;
    
    private TreadSafeStack只需查看代码-总体印象:

    让Task.Run运行同步方法有点尴尬,而且通常认为这不是任务的好用途

    看起来您可能会丢失工作。例如,由于连接问题或其他原因,您如何处理尚未发送的数据。我认为存在两种开放边缘情况。例如,您似乎无法从多个线程调用Update,而不会丢失某些内容。如果您已离线,会发生什么情况价值,你就要关门了

    我不知道什么是TreadSafeStack。那只是一个样本?你认为ConcurrentQueue可能吗

    参与者之间的关系似乎有些尴尬。一般来说,我认为您应该有一个处理器来完成通信,而工作队列方法只是将工作提交给处理器。处理器将检测代理的状态,并根据需要重新连接

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

    我有一个类似的东西-它只是外壳-我去掉了所有使我的东西不同于你的东西的噪音-但也许你可以从中得到我所说的(我的排队将类似于你的更新)

    类发送器
    {
    ///同步原语
    私有静态对象同步=新对象();
    ///处理器在检查工作之前等待这么长时间(如果未推动)
    private const int waittimeoutmillizes=5000;
    ///处理器等待听到有事情要做
    private ManualResetEvent微调=新的ManualResetEvent(错误);
    ///当收到停止信号且消息处理完成时,处理器将进行设置
    private ManualResetEvent done=新的ManualResetEvent(错误);
    ///当主机想要终止消息队列处理器时将设置的标志
    私有布尔关闭=真;
    ///需要发送的消息队列
    私有队列=新队列();
    ///将消息放入队列中
    公共无效排队(字符串消息)
    {
    锁定(同步)
    {
    如果(关机)抛出新的InvalidOperationException(“关机-不接受消息”);
    排队(消息);
    Set();
    }
    }
    ///自动关机
    公众停车场()
    {
    锁定(同步)
    {
    关机=真;
    Set();
    }
    }
    ///开始队列处理
    公共无效开始()
    {
    如果(等待停机(5000))
    {
    锁定(同步)
    {
    关机=假;
    Reset();
    重置();
    ThreadPool.QueueUserWorkItem(处理器);
    }
    }
    其他的
    {
    抛出新的InvalidOperationException(“无法启动-太糟糕了!”);
    }
    }
    ///停止接受队列上的消息,触发关机并等待工作线程完成。
    /// 
    ///如果线程在指定的时间内停止,则为True,否则为false
    私人布尔等待关机(整数毫秒)
    {
    停止();
    锁定(同步)
    {
    如果(关机)返回true;
    }
    返回完成。WaitOne(毫秒至Ait);
    }
    ///写入消息的工作线程方法
    专用无效处理器(对象状态)
    {
    var processList=new List();//-->我们将从队列中取出的工作
    var cancel=false;//-->关闭的本地表示形式,我们将在锁定时获取
    while(true)
    {
    轻推.WaitOne(WaitTimeOut毫秒);
    锁定(同步)
    {
    取消=关闭;
    while(queue.Any())
    {
    Add(queue.Dequeue());
    }
    重置();
    }
    foreach(processList中的var消息)
    {
    尝试
    {
    //发送到服务。。。
    }
    捕获(例外情况除外)
    {
    //重新连接或做任何适当的事情来处理问题。。。
    }
    如果(取消)中断;
    }
    processList.Clear();
    如果(取消)
    {
    Set();
    返回;
    }
    }
    }
    }
    
    如果运行
    ClientProxyWrapper
    的进程意外终止,所有消息会发生什么情况

    您的消息没有持久性,因此无法保证它们会被传递。如果您使用MSMQ之类的排队系统来存储消息,然后从那里处理它们,那就更好了


    消息队列可以是跨国界的,丢失消息的机会将减少。

    您可能有兴趣阅读“持久消息传递”的相关内容。为什么您的类的名称与您的类不同?ServerConnection是一个输入错误,现在已使用正确的构造函数名称修复
    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;
          }
        }
      }
    }