如果我们使用“临时队列”使用JMS请求/回复机制,该代码是否可以扩展?
截至目前,我们不知道是否每秒支持100个请求,或者每秒支持1000个请求。
以下代码是我正在考虑实施的。它以“同步”方式使用JMS。关键部分是创建“消费者”以指向为此会话创建的“临时队列”的位置。我无法弄清楚使用这种临时队列是否是一种可扩展的设计。
destination = session.createQueue("queue:///Q1");
producer = session.createProducer(destination);
tempDestination = session.createTemporaryQueue();
consumer = session.createConsumer(tempDestination);
long uniqueNumber = System.currentTimeMillis() % 1000;
TextMessage message = session
.createTextMessage("SimpleRequestor: Your lucky number today is " + uniqueNumber);
// Set the JMSReplyTo
message.setJMSReplyTo(tempDestination);
// Start the connection
connection.start();
// And, send the request
producer.send(message);
System.out.println("Sent message:\n" + message);
// Now, receive the reply
Message receivedMessage = consumer.receive(15000); // in ms or 15 seconds
System.out.println("\nReceived message:\n" + receivedMessage);
更新
我遇到了另一种模式,请参阅this blog 我们的想法是使用“常规”队列进行发送和接收。但是对于“同步”调用,为了获得所需的响应(即匹配请求),您可以创建一个使用“选择器”监听接收队列的消费者。
步骤:
// 1. Create Send and Receive Queue.
// 2. Create a msg with a specific ID
final String correlationId = UUID.randomUUID().toString();
final TextMessage textMessage = session.createTextMessage( msg );
textMessage.setJMSCorrelationID( correlationId );
// 3. Start a consumer that receives using a 'Selector'.
consumer = session.createConsumer( replyQueue, "JMSCorrelationID = '" + correlationId + "'" );
因此,这种模式的不同之处在于我们不会为每个新请求创建新的临时队列。 相反,所有响应只来自一个队列,但使用“选择器”来确保每个请求线程只接收到关注的响应。
我认为这里的缺点是你必须使用'选择器'。我还不知道这是否比先前提到的模式更不优选或更优选。思考?
答案 0 :(得分:6)
关于帖子中的更新 - 如果在邮件标题上执行选择器,则选择器非常有效,就像使用关联ID一样。 Spring Integration内部也为implementing a JMS Outbound gateway执行此操作。
答案 1 :(得分:4)
有趣的是,这种可扩展性实际上可能与其他响应所描述的相反。
WebSphere MQ尽可能保存并重用动态队列对象。因此,虽然动态队列的使用不是免费的,但它确实可以很好地扩展,因为当队列被释放时,WMQ需要做的就是将句柄传递给请求新队列实例的下一个线程。在繁忙的QMgr中,当句柄从线程传递到线程时,动态队列的数量将保持相对静态。严格来说,它不如重用单个队列那么快,但它并不坏。
另一方面,即使CORRELID
上的索引速度很快,性能也与索引中的消息数相反。如果队列深度开始构建,它也会有所不同。当应用程序在空队列中使用GET
WAIT
时,没有延迟。但是在深度队列中,QMgr必须搜索现有消息的索引以确定回复消息不在其中。在您的示例中,这是每秒搜索空索引与大索引1,000次之间的区别。
结果是1000个动态队列每个都有一条消息实际上可能比1000个线程获得CORRELID
的单个队列更快,具体取决于应用程序和负载的特征。在建议进行特定设计之前,我建议大规模测试。
答案 2 :(得分:2)
在共享队列上使用选择器关联ID可以很好地扩展到多个使用者。
然而,1000个请求/ s将是很多。如果性能证明是个问题,您可能希望在不同实例之间分配一点负载。
您可能希望详细说明请求与客户端号码。如果客户的数量是< 10并且将保持相当静态,并且请求数量非常高,最具弹性和快速的解决方案可能是为每个客户端提供静态回复队列。
答案 3 :(得分:1)
创建临时队列不是免费的。毕竟它是在代理上分配资源。话虽如此,如果你有一个未知的(事前)可能未绑定的客户端数量(多个JVM,每个JVM有多个并发线程等),你可能没有选择权。分配客户端队列并将它们分配给客户端会很快失控。
当然,您所描绘的是最简单的解决方案。如果你能得到交易量的真实数字并且它足够大,那就好了。
在我考虑避免临时队列之前,我会更多地考虑限制客户端数量并使客户长寿。也就是说在客户端创建一个客户端池,并让池中的客户端在启动时创建临时队列,会话,连接等,在后续请求中重用它们,并在关闭时将其拆除。然后,调优问题成为池中最大/最小大小之一,修剪池的空闲时间,以及池最大化时的行为(失败与阻止)。除非您正在创建任意大量的瞬态JVM(在这种情况下,您只是从JVM启动开销中获得了更大的扩展问题),否则应该扩展以及任何事情。毕竟,此时您分配的资源反映了系统的实际使用情况。真的没有机会少用这个。
要避免的是创建和销毁大量无休止的队列,会话,连接等。设计服务器端以允许从一开始就进行流式传输。然后在需要的时候汇集。就像没有,对于任何非平凡的事情,你都需要。
答案 4 :(得分:0)
使用临时队列每次都会花费每次创建的依赖队列。而不是使用缓存生成器作为静态replyToQueue,createProducer方法将在高度并发的调用环境中成本更高并且影响性能。
答案 5 :(得分:0)
我一直面临着同样的问题,并决定将自己的连接集中在无状态bean中。一个客户端连接有一个tempQueue,它位于JMSMessageExchanger对象(包含connectionFactory,Queue和tempQueue)中,该对象绑定到一个bean实例。我在JSE / EE环境中测试过它。但我不确定Glassfish JMS池的行为。 它是否真的会关闭在bean方法结束后“手动”获得的JMS连接?我做错了什么?
我还关闭了客户端bean中的事务(TransactionAttributeType.NOT_SUPPORTED),以便立即向请求队列发送请求消息。
package net.sf.selibs.utils.amq;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import lombok.Getter;
import lombok.Setter;
import net.sf.selibs.utils.misc.UHelper;
public class JMSMessageExchanger {
@Setter
@Getter
protected long timeout = 60 * 1000;
public JMSMessageExchanger(ConnectionFactory cf) {
this.cf = cf;
}
public JMSMessageExchanger(ConnectionFactory cf, Queue queue) {
this.cf = cf;
this.queue = queue;
}
//work
protected ConnectionFactory cf;
protected Queue queue;
protected TemporaryQueue tempQueue;
protected Connection connection;
protected Session session;
protected MessageProducer producer;
protected MessageConsumer consumer;
//status
protected boolean started = false;
protected int mid = 0;
public Message makeRequest(RequestProducer producer) throws Exception {
try {
if (!this.started) {
this.init();
this.tempQueue = this.session.createTemporaryQueue();
this.consumer = this.session.createConsumer(tempQueue);
}
//send request
Message requestM = producer.produce(this.session);
mid++;
requestM.setJMSCorrelationID(String.valueOf(mid));
requestM.setJMSReplyTo(this.tempQueue);
this.producer.send(this.queue, requestM);
//get response
while (true) {
Message responseM = this.consumer.receive(this.timeout);
if (responseM == null) {
return null;
}
int midResp = Integer.parseInt(responseM.getJMSCorrelationID());
if (mid == midResp) {
return responseM;
} else {
//just get other message
}
}
} catch (Exception ex) {
this.close();
throw ex;
}
}
public void makeResponse(ResponseProducer producer) throws Exception {
try {
if (!this.started) {
this.init();
}
Message response = producer.produce(this.session);
response.setJMSCorrelationID(producer.getRequest().getJMSCorrelationID());
this.producer.send(producer.getRequest().getJMSReplyTo(), response);
} catch (Exception ex) {
this.close();
throw ex;
}
}
protected void init() throws Exception {
this.connection = cf.createConnection();
this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
this.producer = this.session.createProducer(null);
this.producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
this.connection.start();
this.started = true;
}
public void close() {
UHelper.close(producer);
UHelper.close(consumer);
UHelper.close(session);
UHelper.close(connection);
this.started = false;
}
}
在客户端(无状态bean)和服务器(@MessageDriven)中使用相同的类。 RequestProducer和ResponseProducer是接口:
package net.sf.selibs.utils.amq;
import javax.jms.Message;
import javax.jms.Session;
public interface RequestProducer {
Message produce(Session session) throws Exception;
}
package net.sf.selibs.utils.amq;
import javax.jms.Message;
public interface ResponseProducer extends RequestProducer{
void setRequest(Message request);
Message getRequest();
}
此外,我还阅读了有关AMQ请求 - 响应实现的AMQ文章: http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html
答案 6 :(得分:-1)
也许我来不及但是本周我花了几个小时来在JMS中同步请求/回复。如何使用超时扩展QueueRequester。我做过,至少在一台机器(运行代理,请求者和回复者)上进行测试表明,该解决方案优于所讨论的解决方案。另一方面,它依赖于使用QueueConnection,这意味着您可能被迫打开多个Connections。