最近,我一直试图让Reply-To模式在Apache NMS / ActiveMQ中工作,并且仅使用临时队列的名称将消息发送到临时队列时遇到了问题。
项目是调度程序服务,它从总线检索请求并将它们发送到另一个进程/运行时(基于复杂的路由标准)来处理请求。然后,这个单独的处理器使用回复队列名称和相关ID来制作响应并将其发送到同一代理上的原始请求者,但是连接不同。
问题是,如果您拥有来自邮件的NMSReplyTo标头的IDestination对象引用,则您似乎只能发送到临时队列(或主题)。如果该引用丢失,则无法通过简单地使用其名称将消息发送到临时队列(或主题)。
说明此问题的是这个简单的“Pong”服务,该服务侦听消息队列并使用NMS Reply-To标头的内容向请求者发出响应。它模仿通过简单地调用ProcessMessage(string,string)方法将请求分派给另一个进程。
using System;
using Apache.NMS;
namespace PongService
{
/// <summary>Simple request dispatcher which mimics dispatching requests to other workers in "The Cloud"</summary>
class PongService
{
static ISession session = null;
static IMessageProducer producer = null;
public static void Main(string[] args)
{
Uri connecturi = new Uri("activemq:tcp://localhost:61616");
Console.WriteLine("Connecting to " + connecturi);
IConnectionFactory factory = new NMSConnectionFactory(connecturi);
IConnection connection = factory.CreateConnection();
session = connection.CreateSession();
IDestination destination = session.GetQueue("PONG.CMD");
Console.WriteLine("Using destination: " + destination);
producer = session.CreateProducer(null);
IMessageConsumer consumer = session.CreateConsumer(destination);
connection.Start();
consumer.Listener += new MessageListener(OnMessage);
Console.WriteLine("Press any key to terminate Pong service . . .");
// loop until a key is pressed
while (!Console.KeyAvailable)
{
try { System.Threading.Thread.Sleep(50); }
catch (Exception ex) { Console.Error.WriteLine(ex.Message + "\r\n" + ex.StackTrace); }
} // loop
Console.Write("Closing connection...");
consumer.Close();
producer.Close();
session.Close();
connection.Close();
Console.WriteLine("done.");
}
/// <summary>Consumer call-back which receives requests and dispatches them to available workers in 'The Cloud'</summary>
/// <param name="receivedMsg">The message received on the request queue.</param>
protected static void OnMessage(IMessage receivedMsg)
{
// mimic the operation of passing this request to an external processor which can connect
// to the broker but will not have references to the session objects including destinations
Console.WriteLine("Sending request to an external processor");
ProcessMessage(receivedMsg.NMSReplyTo.ToString(), receivedMsg.NMSCorrelationID.ToString());
}
/// <summary>Models a worker in another process/runtime.</summary>
/// <param name="queuename">Where to send the results of processing</param>
/// <param name="crid">Correlation identifier of the request.</param>
protected static void ProcessMessage(string queuename, string crid)
{
ITextMessage response = session.CreateTextMessage("Pong!");
response.NMSCorrelationID = crid;
IDestination destination = session.GetQueue(queuename);
Console.WriteLine("Sending response with CRID of '" + crid + "' to " + queuename + "'");
try
{
producer.Send(destination, response);
}
catch (Exception ex)
{
Console.Error.WriteLine("Could not send response: " + ex.Message);
}
}
}
}
现在为客户。它只是创建一个临时队列,开始监听它,然后在我们的“Pong”服务正在侦听的队列上发送请求。请求消息包含临时队列的IDestination。
using System;
using System.Threading;
using Apache.NMS;
using Apache.NMS.Util;
namespace PongClient
{
class PongClient
{
protected static AutoResetEvent semaphore = new AutoResetEvent(false);
protected static ITextMessage message = null;
protected static TimeSpan receiveTimeout = TimeSpan.FromSeconds(3);
public static void Main(string[] args)
{
Uri connecturi = new Uri("activemq:tcp://localhost:61616");
Console.WriteLine("About to connect to " + connecturi);
IConnectionFactory factory = new NMSConnectionFactory(connecturi);
IConnection connection = factory.CreateConnection();
ISession session = connection.CreateSession();
IDestination temporaryDestination = session.CreateTemporaryQueue();
Console.WriteLine("Private destination: " + temporaryDestination);
IDestination destination = session.GetQueue("PONG.CMD");
Console.WriteLine("Service destination: " + destination);
IMessageConsumer consumer = session.CreateConsumer(destination);
consumer.Listener += new MessageListener(OnMessage);
IMessageProducer producer = session.CreateProducer(destination);
connection.Start();
// Send a request message
ITextMessage request = session.CreateTextMessage("Ping");
request.NMSCorrelationID = Guid.NewGuid().ToString();
request.NMSReplyTo = temporaryDestination;
producer.Send(request);
// Wait for the message
semaphore.WaitOne((int)receiveTimeout.TotalMilliseconds, true);
if (message == null)
{
Console.WriteLine("Timed-Out!");
}
else
{
Console.WriteLine("Received message with ID: " + message.NMSMessageId);
Console.WriteLine("Received message with text: " + message.Text);
}
}
protected static void OnMessage(IMessage receivedMsg)
{
message = receivedMsg as ITextMessage;
semaphore.Set();
}
}
}
Pong进程似乎运行正常,只有它会在Reply-To标头中指定的队列中形成一个全新的独立队列。
以下是所涉及技术的版本:
此问题与描述类似问题的this post有关。希望这些例子有助于澄清该请求中的问题。
对此解决方案的任何帮助或见解将不胜感激。
答案 0 :(得分:1)
您实际上并未在PongClient的请求消息中设置reply-to标头。
试试这个:
ITextMessage request = session.CreateTextMessage("Ping");
request.NMSCorrelationID = Guid.NewGuid().ToString();
request.NMSReplyTo = temporaryDestination;
producer.Send(request);
答案 1 :(得分:0)
您需要使用传递的IDestination
。
调用
IDestination destination = session.GetQueue(queuename);
有点邪恶。在封面下,它调用CreateTemporaryQueue()替换现有的临时队列,使用相同名称的新队列,而不通知您。
答案 2 :(得分:0)
我建议使用主题作为回复目的地,并根据NMSCorrelationID让您的消费者过滤器。这是我在对临时队列感到非常沮丧之后转移到的实现。它实际上有很多优点。