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