我的要求如下: 我必须在队列顶部开发一个包装器服务,所以我只是通过一些消息队列(ActiveMQ,Apollo,Kafka)。但决定继续使用ActiveMQ来匹配我们的用例。现在要求如下:
1)将选择基于clientId队列的不同发布者将通过其发布到队列的restful api。
2)消费者将通过restful api消费消息,并将批量消费消息。说消费者就像从队列中给我10条消息一样。 现在服务应该提供10条消息,如果有10条消息,或者如果消息号小于或者零,它将相应地发送。在收到消息后,客户端将处理该消息并通过不同的res-full uri发回确认。收到该确认后,MQService应该从队列中提交或回滚消息。 为了在MQService层中实现这一点,我使用了一个缓存,其中我保留了JMS连接和会话对象,直到收到确认或ttl到期。
为了批量检索消息并发送回客户端,我创建了一个多线程消费者,这样对于5批消息请求,服务层将创建5个线程,每个线程具有不同的连接和会话对象(如在ActiveMQ多消费者http://activemq.apache.org/multiple-consumers-on-a-queue.html)
中说明基本用例:
MQ(BROKER) [A] - >包装器(MQService) [B] - >客户端 [C]
注意: [B]是一个restfull服务,其中实现了JMS使用者。它将连接和会话对象保留在缓存中。
[C]请求[B]给出3条消息 如果在队列中可用,[B]必须获取3条消息,将其包装在batchmsgFormat中并将其发送到[C] [C]处理消息并通过/ send-ack uri发送确认/失败到[B]。 从[C]接收Ack后,[B]将提交Jms会话并关闭会话和连接对象。它也将从缓存中逐出。
上述工作流程正常,单个邮件提取 但是当尝试使用多线程获取多个消费者的消息时,队列挂起在JMS MesageConsumer.receive()上。 ...
这里是MQService层中的JMS Consumer代码: ----------------------------------------------
public BatchMessageFormat getConsumeMsg(final String clientId, final Integer batchSize) throws Exception {
BatchMessageFormat batchmsgFormat = new BatchMessageFormat();
List<MessageFormat> msgdetails = new ArrayList<MessageFormat>();
List<Future<MessageFormat>> futuremsgdetails = new ArrayList<Future<MessageFormat>>();
if (batchSize != null) {
Integer msgCount = getMsgCount(clientId, batchSize);
for (int batchconnect = 0; batchconnect <msgCount; batchconnect++) {
FutureTask<MessageFormat> task = new FutureTask<MessageFormat>(new Callable<MessageFormat>() {
@Override
public MessageFormat call() throws Exception {
MessageFormat msg=consumeBatchMsg(clientId,batchSize);
return msg;
}
});
futuremsgdetails.add(task);
Thread t = new Thread(task);
t.start();
}
for(Future<MessageFormat> msg:futuremsgdetails){
msgdetails.add(msg.get());
}
batchmsgFormat.setMsgDetails(msgdetails);
return batchmsgFormat
}
邮件提取:
private MessageFormat consumeBatchMsg(String clientId, Integer batchSize) throws JMSException, IOException{
MessageFormat msgFormat= new MessageFormat();
Connection qC = ConnectionUtil.getConnection();
qC.start();
Session session = qC.createSession(true, -1);
Destination destination = createQueue(clientId, session);
MessageConsumer consumer = session.createConsumer(destination);
Message message = consumer.receive(2000);
if (message!=null || message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
msgFormat.setMessageID(textMessage.getJMSMessageID());
msgFormat.setMessage(textMessage.getText());
CacheObject cacheValue = new CacheObject();
cacheValue.setConnection(qC);
cacheValue.setSession(session);
cacheValue.setJmsQueue(destination);
MQCache.instance().add(textMessage.getJMSMessageID(),cacheValue);
}
consumer.close();
return msgFormat;
}
确认和会话结束:
public String getACK(String clientId,String msgId,String ack)throws JMSException{
if (MQCache.instance().get(msgId) != null) {
Connection connection = MQCache.instance().get(msgId).getConnection();
Session session = MQCache.instance().get(msgId).getSession();
Destination destination = MQCache.instance().get(msgId).getJmsQueue();
MessageConsumer consumer = session.createConsumer(destination);
if (ack.equalsIgnoreCase("SUCCESS")) {
session.commit();
} else {
session.rollback();
}
session.close();
connection.close();
MQCache.instance().evictCache(msgId);
return "Accepted";
} else {
return "Rejected";
}
}
有没有人在类似情况下工作过,还是你可以投光?有没有其他方法可以实现此批量消息提取以及客户端故障处理?
答案 0 :(得分:0)
我将提供一些指示,以帮助更好地编写此逻辑。
我假设您尽可能使用纯JMS 1.1。确保您有一个从池中获取连接或创建连接的位置。你不需要在一个线程内做到这一点。你可以在外面做。必须在线程内创建会话,不应共享。这将影响函数consumeBatchMsg()中的逻辑。
其次,使用一个线程消耗给定batchSize的所有消息更简单。我看到你正在使用事务处理会话。因此,您可以在获取batchSize的所有消息后执行一次提交。
如果你真的想采取在队列中拥有多个使用者的复杂路径(可能性能稍差),可以使用Java的CountDownLatch或CyclicBarrier并将其设置为batchSize来触发。一旦所有线程都收到了消息,它就可以提交并关闭相应线程中的会话。永远不要让会话实例超出创建它的线程的上下文。
答案 1 :(得分:0)
在将预取限制设置为0后尝试,如下所示:
ConnectionFactory connectionFactory
= new ActiveMQConnectionFactory("tcp://localhost:61616?jms.prefetchPolicy.queuePrefetch=0");