我已经编写了一个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);
}
}
}
答案 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这样的排队系统来存储消息然后从那里处理它们会更好。
消息队列可以是跨国的,并且减少丢失消息的可能性。