![](/img/trans.png)
[英]How to enforce message queue sequence with multiple WCF service instances
[英]How can my WCF service recover from unavailable message queue?
我有一个WCF服务,该服务从Microsoft消息队列( netMsmqBinding
)接收消息。
如果消息队列不可用,我希望我的服务恢复。 我的代码应该无法打开服务,但是在延迟后再试一次。
当队列不可用时,我有代码可以识别错误:
static bool ExceptionIsBecauseMsmqNotStarted(TypeInitializationException ex)
{
MsmqException msmqException = ex.InnerException as MsmqException;
return ((msmqException != null) && msmqException.HResult == (unchecked((int)(0xc00e000b))));
}
因此,这应该很简单:我调用ServiceHost.Open()
,捕获此异常,等待一两秒钟,然后重复执行直到我的Open
调用成功为止。
问题是,如果此异常被抛出一次,它将继续被抛出。 消息队列可能已经可用,但是我正在运行的进程处于错误状态,并且我继续获取TypeInitializationException
直到我关闭进程并重新启动它。
有办法解决这个问题吗? 我可以让WCF原谅队列并真正尝试再次收听吗?
这是我的服务开始代码:
public async void Start()
{
try
{
_log.Debug("Starting the data warehouse service");
while(!_cancellationTokenSource.IsCancellationRequested)
{
try
{
_serviceHost = new ServiceHost(_dataWarehouseWriter);
_serviceHost.Open();
return;
}
catch (TypeInitializationException ex)
{
_serviceHost.Abort();
if(!ExceptionIsBecauseMsmqNotStarted(ex))
{
throw;
}
}
await Task.Delay(1000, _cancellationTokenSource.Token);
}
}
catch (Exception ex)
{
_log.Error("Failed to start the service host", ex);
}
}
这是堆栈信息。 第一次引发内部异常的堆栈跟踪是:
以及外部异常堆栈的顶部条目:
在System.ServiceModel.Channels.MsmqChannelListenerBase`1.get_TransportManagerTable()
在System.ServiceModel.Channels.TransportManagerContainer..ctor(TransportChannelListener侦听器)
微软已经使WCF的源代码可见,因此现在我们可以准确地了解正在发生的事情。
坏消息:WCF的实现方式是,如果对ServiceModel.Start()
的初始调用触发了排队错误,则无法恢复。
WCF框架包括一个称为MsmqQueue
的内部类。 此类具有静态构造函数 。 静态构造函数调用GetMsmqInformation ,这可能引发异常。
阅读有关静态构造函数的C#编程指南 :
如果静态构造函数引发异常,则运行时不会再次调用该异常,并且该类型将在程序运行所在的应用程序域的生存期内保持未初始化状态。
这里有一个编程课:不要将引发异常的代码放在静态构造函数中!
显而易见的解决方案位于代码之外。 创建托管服务时,可以在消息队列服务上添加服务依赖项。 但是,我宁愿先使用代码然后进行配置来解决此问题。
另一个解决方案是使用非WCF代码手动检查队列是否可用。
如果消息队列服务不可用,则方法System.Messaging.MessageQueue.Exists返回false
。 知道这一点,以下工作:
private const string KNOWN_QUEUE_PATH = @".\Private$\datawarehouse";
private static string GetMessageQueuePath()
{
// We can improve this by extracting the queue path from the configuration file
return KNOWN_QUEUE_PATH;
}
public async void Start()
{
try
{
_log.Debug("Starting the data warehouse service");
string queuePath = GetMessageQueuePath();
while(!_cancellationTokenSource.IsCancellationRequested)
{
if (!(System.Messaging.MessageQueue.Exists(queuePath)))
{
_log.Warn($"Unable to find the queue {queuePath}. Will try again shortly");
await Task.Delay(60000, _cancellationTokenSource.Token);
}
else
{
_serviceHost = new ServiceHost(_dataWarehouseWriter);
_serviceHost.Open();
return;
}
}
}
catch(System.OperationCanceledException)
{
_log.Debug("The service start operation was cancelled");
}
catch (Exception ex)
{
_log.Error("Failed to start the service host", ex);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.