我不是程序员,但我试图通过给他们一些指导来帮助他们。我们不再拥有msmq的内部专业知识。我们正在尝试使用它来将一些函数与调度应用程序集成。
计划应用程序通过使用自定义构建的dll制作网络摄像头来启动作业。该DLL调用weburl。 Web应用程序将运行其任务并向网站发送有关其执行任务的更新。网站将消息写入队列。调用该站点的dll正在监视队列中是否有分配给该作业的标签的消息。当它收到最终状态消息时它会关闭。
我们每隔几个小时收到以下消息。我们每小时运行近100个使用此方法的作业。在底部列出的代码中,jobid对应于消息队列中消息的标签。每个作业在开始时都会发出一个jobid,并将其用作发送给该作业的msmq的每条消息的标签。
System.Messaging.MessageQueueException (0x80004005): Message that the cursor is currently pointing to has been removed from the queue by another process or by another call to Receive without the use of this cursor.
at System.Messaging.MessageQueue.ReceiveCurrent(TimeSpan timeout, Int32 action, CursorHandle cursor, MessagePropertyFilter filter, MessageQueueTransaction internalTransaction, MessageQueueTransactionType transactionType)
at System.Messaging.MessageEnumerator.get_Current()
以下是代码。
while ( running )
{
// System.Console.WriteLine( "Begin Peek" );
messageQueue.Peek();
//System.Console.WriteLine( "End Peek" );
messageQueue.MessageReadPropertyFilter.SetAll();
using ( MessageEnumerator enumerator = messageQueue.GetMessageEnumerator2() )
{
enumerator.Reset();
while ( enumerator.MoveNext() )
{
Message msg = enumerator.Current;
if ( msg.Label.Equals( this.jobid ) )
{
StringBuilder sb = new StringBuilder();
/*
try
{
sb.Append( "Message Source: " );
//sb.Append( msg.SourceMachine );
sb.Append( " Sent: " );
sb.Append( msg.SentTime );
sb.Append( " Label " );
sb.Append( msg.Label );
sb.Append( " ID: " );
sb.Append( msg.Id );
sb.Append( " CorrelationID: " );
sb.Append( msg.CorrelationId );
sb.Append( " Body Type: " );
sb.Append( msg.BodyType );
}
catch ( Exception )
{
throw;
}
finally
{
System.Console.WriteLine( sb.ToString() );
}
*/
//System.Console.WriteLine( "Receiving Message started" );
using ( Message message = messageQueue.ReceiveById( msg.Id ) )
{
//System.Console.WriteLine( "Receiving Message Complete" );
//sb = new StringBuilder();
string bodyText = string.Empty;
try
{
System.IO.StringWriter sw = new System.IO.StringWriter( sb );
System.IO.StreamReader sr = new System.IO.StreamReader( message.BodyStream );
while ( !sr.EndOfStream )
{
sw.WriteLine( sr.ReadLine() );
}
sr.Close();
sw.Close();
bodyText = ( string ) FromXml( sb.ToString(), typeof( string ) );
int indx = bodyText.IndexOf( ',' );
string tokens = bodyText.Substring( indx + 1 );
indx = tokens.IndexOf( ',' );
string command = tokens.Substring( 0, indx );
tokens = tokens.Substring( indx + 1 );
if ( command.Equals( COMMAND_STARTED ) )
{
System.Console.WriteLine( "STARTED " + tokens );
}
else if ( command.Equals( COMMAND_UPDATE ) )
{
System.Console.WriteLine( tokens );
}
else if ( command.Equals( COMMAND_ENDED_OK ) )
{
System.Console.WriteLine( tokens );
System.Console.WriteLine( "WEBJOB: Success" );
finalResults = new FinalResults( 0, 0, "Success" );
running = false;
}
else if ( command.Equals( COMMAND_ENDED_WARNING ) )
{
System.Console.WriteLine( tokens );
System.Console.WriteLine( "WEBJOB: Warning Issued" );
finalResults = new FinalResults( 1, 1, "Warning" );
running = false;
}
else if ( command.Equals( COMMAND_ENDED_FAIL ) )
{
System.Console.WriteLine( tokens );
System.Console.WriteLine( "WEBJOB: Failure" );
finalResults = new FinalResults( 2, 16, "Failure" );
running = false;
}
}
catch ( Exception )
{
throw;
}
finally
{
//System.Console.WriteLine( "Body: " + bodyText );
}
}
}
}
}
}
return finalResults;
}
MessageQueue messageQueue = null;
string webServiceURL = "";
Dictionary<string, string> parms = new Dictionary<string, string>();
string jobid = "NONE";
答案 0 :(得分:3)
这通常意味着在接收操作完成之前,您正在接收的消息被其他内容删除。另一个应用程序,或与您的代码使用不同队列引用的同一进程中的另一个线程。
是否有可能同时运行两个处理器代码实例(我猜它是一个控制台应用程序)?在相同或不同的机器上?或者其他一些应用程序或工具从队列中删除消息?
.NET 2.0的一个预发布版本中曾经存在一个错误,它会在某些压力条件下导致这种错误,但据我记得它在发布之前已经修复了。
答案 1 :(得分:3)
kprobst的解释很可能是正在发生的事情。即使您看到此特定消息在队列中,如果其他应用程序(或同一应用程序的不同实例)从此队列中选择(任何)消息,也会使游标无效。
如果多个进程从同一队列中进行处理,则此代码本身并不适用。
答案 2 :(得分:2)
由于MessageQueue的内部方法ReceiveCurrent中存在并发问题,因此失败。 异常堆栈跟踪显示调用源自enumerator.Current行,异常发生在ReceiveCurrent。 Enumerator.Current调用ReceiveCurrent&#34; peek&#34;选项。你可以问一下,当我遇到同样的问题时,我也可以问一下,#34;消息是否已收到&#34;错误?它只是试图查看尚未收到的下一条消息? 好的答案在于ReceiveCurrent代码,可以在这里查看: https://referencesource.microsoft.com/#System.Messaging/System/Messaging/MessageQueue.cs,02c33cc512659fd7,references
ReceiveCurrent首先进行StaleSafeReceive调用以查看下一条消息。但是如果这个调用返回它需要更多的内存来接收整个消息(带有的行 &#34; while(MessageQueue.IsMemoryError(status)&#34;在其源代码中),它分配所需的内存并进行另一个StaleSafeReceive调用以获取消息。 这是非常经典的Win32 API使用模式,因为它最终是基于C的。
这里的问题是,如果在ReceiveCurrent内部第一次和第二次调用StaleSafeReceive之间另一个进程或线程&#34;接收&#34;,即从队列中删除该消息,则第二次调用会抛出这个确切的异常。这就是&#34;偷看&#34;操作失败。 请注意,它可能是导致异常的枚举器扫描的任何消息,而不是正在查找的消息。这解释了为什么在抛出异常并且方法失败后,具有该作业id的消息仍然存在于队列中。
可以做的是使用try catch保护enumerator.Current调用,如果捕获到这个特殊异常,只需继续使用队列中下一个可用消息的枚举。
我使用过Cursor对象而不是枚举器,但它遇到了同样的问题。但是使用Cursor还有另一种方法可以降低发生这种情况的风险,即扫描/查看消息时是关闭当前Queue对象的MessagePropertyFilter的所有不需要的属性,尤其是Body属性。因为在窥视期间通常不需要接收主体,但是通常消息的主体导致重新分配内存并且需要在ReceiveCurrent内部进行第二次StaleSafeReceive调用。 对于这个异常,还需要使用直接Cursor,并使用peek调用。