我有一个使用MSMQ进行异步处理的应用程序。
我使用WCF将消息放入队列并拥有一个WCF MSMQ监听器(Windows服务)来接收消息并处理它们。
我的问题是保持稳定。处理(例如)队列服务器(这是一个单独的盒子)的正确方法是什么?前几天发生这种情况并且服务只是坐在那里 - 没有抛出任何异常,它只是停止接收消息。 我希望它在队列服务器关闭时抛出异常,然后重新尝试连接到它,直到它能够。
我还注意到,在服务上执行“停止”通常会导致它在最终停止之前挂起一段时间。
任何代码建议或批评都是受欢迎的。显然我首先为谷歌做了这个,但是大多数例子都向我展示了我已经拥有的东西,并且我想让我的系统比那更强大。
目前我有这个:
(注意:IMyExampleServiceContract是我的WCF服务合同,而QueueHandler是实现它的东西)
namespace xyz.MyExample.MSMQListener
{
/// <summary>
/// The class that handles starting and stopping of the WCF MSMQ Listener windows service.
/// It will respond to start and stop commands from within the windows services administration snap-in
/// It creates a WCF NetMsmqBinding that watches a particular queue for messaages defined by a contract
/// in the ServiceContracts project.
/// </summary>
public partial class MsmqListenerService : ServiceBase
{
/// <summary>
/// The WCF service host
/// </summary>
private ServiceHost _serviceHost;
/// <summary>
/// Defines the maximum size for a WCF message
/// </summary>
private const long MaxMessageSize = 1024 * 1024 * 1024; // 1 gb
/// <summary>
/// Defines the maximum size for a WCF array
/// </summary>
private const int MaxArraySize = 1024 * 1024 * 1024; // 1 gb
/// <summary>
/// The queue name
/// </summary>
private readonly string _queueName;
/// <summary>
/// The queue server
/// </summary>
private readonly string _queueServer;
/// <summary>
/// Initializes a new instance of the <see cref="MsmqListenerService"/> class.
/// </summary>
public MsmqListenerService()
{
InitializeComponent();
using (ConfigManager config = new ConfigManager())
{
_queueName = config.GetAppSetting("QueueName");
_queueServer = config.GetAppSetting("QueueServer");
}
}
/// <summary>
/// When implemented in a derived class, executes when a Start command is sent to the service by the Service Control Manager (SCM) or when the operating system starts (for a service that starts automatically). Specifies actions to take when the service starts.
/// <para>
/// The logic in this method creates a WCF service host (i.e. something that listens for messages) using the <see cref="IMyExampleServiceContract"/> contract.
/// The WCF end point is a NetMSMQBinding to the MyExample MSMQ server/queue.
/// It sets up this end point and provides a class to handle the messages received on it.
/// The NetMSMQBinding is a Microsoft WCF binding that handles serialisation of data to MSMQ. It is a ms proprietary format and means that the message on the queue
/// can only be read by a WCF service with the correct contract information.
/// </para>
/// </summary>
/// <param name="args">Data passed by the start command.</param>
protected override void OnStart(string[] args)
{
try
{
Logger.Write("MyExample MSMQ listener service started.", StandardCategories.Information);
Uri serviceUri = new Uri("net.msmq://" + QueueServer + QueueName);
NetMsmqBinding serviceBinding = new NetMsmqBinding();
serviceBinding.Security.Transport.MsmqAuthenticationMode = MsmqAuthenticationMode.None;
serviceBinding.Security.Transport.MsmqProtectionLevel = System.Net.Security.ProtectionLevel.None;
serviceBinding.MaxReceivedMessageSize = MaxMessageSize;
serviceBinding.ReaderQuotas.MaxArrayLength = MaxArraySize;
//QueueHandler implements IMyExampleServiceContract
_serviceHost = new ServiceHost(typeof(QueueHandler));
_serviceHost.AddServiceEndpoint(typeof(IMyExampleServiceContract), serviceBinding, serviceUri);
_serviceHost.Open();
Logger.Write("MyExample MSMQ listener service completed OnStart method.", StandardCategories.Information);
}
catch (Exception ex)
{
ExceptionReporting.ReportException(ex, "DefaultExceptionPolicy");
throw;
}
}
/// <summary>
/// Gets the name of the queue to send to.
/// This is retrieved from the application settings under QueueName
/// </summary>
private string QueueName
{
get { return _queueName; }
}
/// <summary>
/// Gets the name of the queue server to send to.
/// This is retrieved from the application settings under QueueServer
/// </summary>
private string QueueServer
{
get { return _queueServer; }
}
/// <summary>
/// When implemented in a derived class, executes when a Stop command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service stops running.
/// </summary>
protected override void OnStop()
{
if (_serviceHost != null)
{
_serviceHost.Close();
_serviceHost = null;
}
}
/// <summary>
/// The main entry point for the application.
/// </summary>
public static void Main()
{
//Code will have to be compiled in release mode to be installed as a windows service
#if (!DEBUG)
try
{
Logger.Write("Attempting to start queue listener service.", StandardCategories.Information);
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MsmqListenerService()
};
ServiceBase.Run(ServicesToRun);
Logger.Write("Finished ServiceBase.Run of queue listener service.", StandardCategories.Information);
}
catch (Exception e)
{
ExceptionReporting.ReportException(e, "DefaultExceptionPolicy");
throw;
}
#else
//This allows us to run from within visual studio
MsmqListenerService service = new MsmqListenerService();
service.OnStart(null);
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#endif
}
}
}
答案 0 :(得分:5)
我不确定为什么你的服务主机会挂起,但我绝对可以想到一些让它更可靠的事情:
a)它可以成功发送它们
b)正在侦听该队列的本地WCF运行状况服务正在拾取和处理这些消息。这可用于检测一些可能的错误情况。
答案 1 :(得分:3)
底层WCF MSMQ侦听器可能在它到达您的代码之前抛出异常。这是令人沮丧的情况,因为它看起来没有任何反应,最糟糕的是你的消息被丢弃。打开服务配置文件中的WCF service tracing。
现在,当您运行服务时,它将跟踪并提供更多详细信息。而不是通过XML使你的眼睛紧张,用MS服务跟踪查看器打开这个日志文件。
当我遇到这个问题时,我收到了“System.ServiceModel.ProtocolException”:
传入的MSMQ消息在其正文中包含无效或意外的.NET消息帧信息。无法接收消息。确保发件人使用与匹配的SessionMode *兼容的服务合同。我的服务合同已更改为具有SessionMode = SessionMode.Required属性,但客户端未发送带有事务的消息。
答案 2 :(得分:1)
尽管WCF为MSMQ添加了一些很酷的功能,但有时您可以轻松实现目标,并且如果手动编写MSMQ处理代码,则可以更好地控制。
如果您手动处理队列,您将能够准确地看到正在发生的事情&amp;处理抛出的MessageQueueExceptions,例如你将能够捕获MessageQueueErrorCodes,例如QueueNotFound或MachineNotFound。
不幸的是,这意味着还要管理有害消息队列,向处理添加事务,向队列添加超时时间等等.WCF很好地为您处理所有这些事情。
使用WCF的主要好处是,您可以使用WAS实例化Web应用程序,而不是持续运行Windows服务。如果你没有利用这个好处,那么我并没有真正看到WCF带来任何好处 - 它只是抽象出你需要触摸和看到的很多东西。
就像旁注一样;也许您可以在将消息放入队列时检查服务器是否可用?如果服务器可以将消息放入队列,则侦听器将能够立即处理它们。