启用多线程JMS事务的消费者挂起

时间:2014-07-11 14:10:21

标签: multithreading jms activemq batch-processing

我的要求如下: 我必须在队列顶部开发一个包装器服务,所以我只是通过一些消息队列(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";
        }

    }

有没有人在类似情况下工作过,还是你可以投光?有没有其他方法可以实现此批量消息提取以及客户端故障处理?

2 个答案:

答案 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");