0

ActiveMQ/Spring experts,

I am running into a very strange problem with ActiveMQ and DefaultMessageListenerContainer/SimpleMessageListenerContainer combination. We have a web application built using Spring (we are at 4.x). One of the transactions is related to processing a bulk upload of files and each file has a number of lines. Each line will become a message for processing.

A publisher publishes each line as a message to a persistent Queue. When examined through the ActiveMQ console we can see the message in the queue. To the same queue, we have a group of listeners configured using DefaultMessageListenerContainer(DMLC)/SimpleMessageListenerContainer(SMLC) (tried both) for consuming messages.

When the publisher publishes 100 messages sometimes only 99 or 98 messages are delivered but the rest are stuck in queue. The configuration is as follows:

  1. ActiveMQ broker is running in standalone mode not networked or embedded in WildFly.
  2. In the Spring application, we tried both DMLC and SMLC but both ran into this issue. Tried simpleMQConnectionFactory as well as PooledConnectionFactory and both times ran into same problem.
  3. Tried setting the prefetch limit to "1" on the PooledConnectionFactory and ran into the same problem. Spring SMLC is throwing an exception when set to "0".
  4. Max concurrent consumers is set to 50
  5. When messages are stuck, if we restart WildFly the remaining messages in the queue are delivered to the consumers.
  6. We are not using transacted sessions rather set the acknowledgModeName = "CLIENT_ACKNOWLEDGE"
  7. We initialize the queue using a spring bean and use that for initializing the SMLC or DMLC

I am running out of options to try at this. If you share your experiences in this regard it is highly appreciated. This application is in production and the problem happens almost every other day sometimes multiple times in a day.

private void publishDMRMessage(DmrDTO p_dmrDTO, long jobID, int numDMRs) {
            //Create a DMR message for each of the unique keys and publish it to 
            try {

                DMRImportMessage message = new DMRImportMessage();
                message.setDmrDTO(p_dmrDTO);
                message.setDmrKey(p_dmrDTO.toString());
                message.setDmrImportJobID(new Long(jobID));
                message.setTask(Command.INITIALIZE_DMR_FORM);
                message.setNumDMRForms(new Long(numDMRs));
                sender.sendMessage(message);
            } catch (Exception e) {
                System.out.println(" JMS Exception = " + e.getMessage());
                e.printStackTrace();
            }
     }

public class DMRMessageListener implements MessageListener {

    private DMRImportManager manager;

    private JMSMessageSender    sender;

    private DmrFormInitService  formService;

    private ProcessDMRValidationMessage validateService;

    private ImportDmrService    dmrService;

    private static final Logger log = Logger.getLogger(DMRMessageListener.class);

    public ImportDmrService getDmrService() {
        return dmrService;
    }

    public void setDmrService(ImportDmrService dmrService) {
        this.dmrService = dmrService;
    }

    public ProcessDMRValidationMessage getValidateService() {
        return validateService;
    }

    public void setValidateService(ProcessDMRValidationMessage validateService) {
        this.validateService = validateService;
    }

    public DmrFormInitService getFormService() {
        return formService;
    }

    public void setFormService(DmrFormInitService formService) {
        this.formService = formService;
    }

    public JMSMessageSender getSender() {
        return sender;
    }

    public void setSender(JMSMessageSender sender) {
        this.sender = sender;
    }

    public DMRImportManager getManager() {
        return manager;
    }

    public void setManager(DMRImportManager manager) {
        this.manager = manager;
    }


    public void onMessage(Message message) {
        if (message instanceof ObjectMessage) {
            try {
                ObjectMessage objectMessage = (ObjectMessage) message;
                DMRImportMessage dmrMessage =  (DMRImportMessage)objectMessage.getObject();

                log.info("============= Message Received =========================");
                log.info("Message Type = " + dmrMessage.getTask() + " for JOB ID = " + dmrMessage.getDmrImportJobID());
                log.info("Message Received === DMR ID = " + dmrMessage.getDmrID());
                log.info("Message Received === DMR key = " + dmrMessage.getDmrKey());
                log.info("============= Message Received =========================");

                //Process the message
                processDMRMessage(dmrMessage);

                DMRProcessingStatus status = manager.updateStatus(dmrMessage);

                if (status.isStatus()) {
                    log.info(" One stage is complete, the next stage should start for JOB ID = " + dmrMessage.getDmrImportJobID());
                    publishMessageForNextStepOfProcessing(dmrMessage, status);
                }

            }
            catch (Exception ex) {
                ex.printStackTrace();
                throw new RuntimeException(ex);
            }
        }
        else {
            log.error(" *****  Received an invalid message -- NOT AN Object message so cannot be processed and will result in stuck jobs **** ");
            throw new IllegalArgumentException("Message must be of type ObjectMessage");
        }

        //Send the next message in the chain
    }

    /**
     * It will examine the message content and based on the message type it will invoke the appropriate
     * service.
     * 
     * @param dmrMessage DMRImportMessage 
     */
    private void processDMRMessage(DMRImportMessage dmrMessage) {

            if (dmrMessage.getTask() == Command.INITIALIZE_DMR_FORM) {

                Map<String, String> dmrInitResults = formService.initDmrForm(dmrMessage.getDmrDTO());
                //Indicate in message that this DMR Key is not in ICIS
                if (dmrInitResults != null) {
                    if (StringUtils.equalsIgnoreCase(dmrInitResults.get("wsUnscheduleDmrError"), "truee")) {
                        log.info("DMR Key is not in ICIS: " + dmrMessage.getDmrDTO().toString());
                        dmrMessage.setDmrKeyInICIS(false);
                    } else if (StringUtils.equalsIgnoreCase(dmrInitResults.get("wsDBDown"), "truee")) {
                        log.error("Web Service call failed for DMR Key: " + dmrMessage.getDmrDTO().toString());
                    }
                }

            }

            try {
                if (dmrMessage.getTask() == Command.IMPORT_DMR_PARAMETER)  {
                    //Process the Parameter line
                    ParameterProcessingStatus status = dmrService.processLine(dmrMessage.getDmrImportJobID(), dmrMessage.getDmrParameterSubmission(), new Integer(dmrMessage.getLineNumber()), dmrMessage.getDmrKeysNotInICIS());
                    System.out.println("LINE = " + dmrMessage.getLineNumber() + "  Status = " + status.isStatus());
                    dmrMessage.setProcessingStatus(status.isStatus());
                    dmrMessage.setDmrID(status.getDmrID());
                    dmrMessage.setDmrComment(status.getDmrComment());                   
                    return;
                }
            } catch(Exception e) {
                log.error("An exception occurred during processing of line " + dmrMessage.getLineNumber() + " in job " + dmrMessage.getDmrImportJobID());
                e.printStackTrace();
                dmrMessage.setProcessingStatus(false);
                dmrMessage.setDmrID(0L);
            }

            try {
                if (dmrMessage.getTask() == Command.END_DMR_PARAMETER_IMPORT) {
                    //Process the Parameter line
                    //ParameterProcessingStatus status = dmrService.processLine(dmrMessage.getDmrImportJobID(), dmrMessage.getDmrParameterSubmission(), 100);
                    dmrMessage.setProcessingStatus(true);
                    dmrMessage.setDmrID(0L);
                    return;
                }
            } catch(Exception e) {
                e.printStackTrace();
                dmrMessage.setProcessingStatus(false);
                dmrMessage.setDmrID(0L);
            }

            try {
                if (dmrMessage.getTask() == Command.POST_PROCESS_DMR) {
                    //Validate DMRs
                    validateService.validateDMR(dmrMessage);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    }

    private void publishMessageForNextStepOfProcessing(DMRImportMessage dmrMessage, DMRProcessingStatus status) throws JMSException {
            log.info(" =========== Publish a message for next step of processing for JOB ID = " + dmrMessage.getDmrImportJobID());

            if (dmrMessage.getTask() == Command.INITIALIZE_DMR_FORM) {
                //Start the DMR Parameter Processing
                sender.sendDMRControlMessage(this.createControlMessage(ProcessPhase.START_PARAMETER_PROCESSING, dmrMessage.getDmrImportJobID(), status.getDmrKeysNotInICIS()));
                return;
            }

            if ((dmrMessage.getTask() == Command.IMPORT_DMR_PARAMETER)
                    || (dmrMessage.getTask() == Command.END_DMR_PARAMETER_IMPORT)) {
                //Start the DMR Validation Process
                dmrService.postProcessParameters(dmrMessage.getDmrImportJobID(), status.getSuccessfulLines(), status.getErroredLines());
                DMRImportControlMessage message = this.createControlMessage(ProcessPhase.START_DMR_VALIDATION, dmrMessage.getDmrImportJobID());
                message.setDmrIDsWithComments(status.getDmrIDsWithComments());
                sender.sendDMRControlMessage(message);
                return;
            }

            if (dmrMessage.getTask() == Command.POST_PROCESS_DMR) {
                //Start the next DMR import process
                sender.sendDMRControlMessage(this.createControlMessage(ProcessPhase.START_DMR_FORM_INIT, dmrMessage.getDmrImportJobID()));
                return;
            }

            log.info(" =========== End Publish a message for next step of processing for JOB ID = " + dmrMessage.getDmrImportJobID());
    }

    private DMRImportControlMessage createControlMessage(DMRImportControlMessage.ProcessPhase phase, Long jobID) {
            return createControlMessage(phase, jobID, null);
    }

    private DMRImportControlMessage createControlMessage(DMRImportControlMessage.ProcessPhase p_phase, Long p_jobID, Set<DmrDTO> p_dmrDTOs) {
        DMRImportControlMessage message = new DMRImportControlMessage();
        message.setDmrImportJobID(p_jobID);
        message.setPhase(p_phase);

        if (p_dmrDTOs != null) {
            message.setDmrKeysNotInICIS(p_dmrDTOs);
        }

        return message;
    }
//Bean Configs.

<bean id="prefetchPolicy" class="org.apache.activemq.ActiveMQPrefetchPolicy">
<property name="queuePrefetch" value="0"/>
</bean>

<bean id="jmsFactoryPub" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="tcp://localhost:61616" />
</bean>

<bean id="jmsFactoryReceive" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="tcp://localhost:61616" />
<property name="prefetchPolicy" ref="prefetchPolicy" />
</bean>

<bean id="jmsFactoryControlMsg" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL">
      <value>tcp://localhost:61616</value>
    </property>
</bean>
<bean id="dmrQueue" 
    class="org.apache.activemq.command.ActiveMQQueue">
    <constructor-arg value="DMRQueue" />
</bean> 

<bean id="dmrControlQueue" 
    class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="DMRControlQueue" />
</bean>    

<bean id="jmsQueueTemplate"  class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="jmsFactoryPub" />
</bean>

<bean id="jmsQueueTemplateControlMsg"  class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="jmsFactoryControlMsg" />
</bean>

<bean id="messageCreator"  class="net.exchangenetwork.netdmr.service.DMRMessageCreator">
</bean>

<bean id="dmrMessageListener"  class="net.exchangenetwork.netdmr.service.DMRMessageListener">
    <property name="manager" ref="dmrImportManager"/>
    <property name="sender" ref="messagePublisher"/>
    <property name="formService" ref="dmrFormInit"/>
    <property name="validateService" ref="dmrValidator"/>
    <property name="dmrService" ref="importDmrService"/>
</bean>


<bean id="messageSender"  class="net.exchangenetwork.netdmr.service.JMSMessageSender">
    <property name="jmsTemplate" ref="jmsQueueTemplate" />
    <property name="sendQueue" ref="dmrQueue" />
    <property name="creator" ref="messageCreator" />
</bean>


<bean id="messagePublisher"  class="net.exchangenetwork.netdmr.service.JMSMessageSender">
    <property name="jmsTemplate" ref="jmsQueueTemplateControlMsg" />
    <property name="sendQueue" ref="dmrControlQueue" />
    <property name="creator" ref="messageCreator" />
</bean>

   <bean id="jmsContainer"  class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="jmsFactoryReceive"/>
        <!-- this is the queue we will listen on -->
        <property name="destination" ref="dmrQueue" />
        <property name="messageListener" ref="dmrMessageListener"/>
        <property name="concurrentConsumers" value="60"/>
        <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE"/>
        <property name="errorHandler" ref="jmsErrorHandler"/>
        <property name="exceptionListener" ref="jmsExceptionHandler"/>
        <property name="receiveTimeout" value="0"/>
    </bean>
Roger
  • 1
  • 2
  • have you tried with acknowledgModeName = "AUTO_ACKNOWLEDGE" – Hassen Bennour Feb 14 '18 at 08:20
  • can you post your code and logs – Hassen Bennour Feb 14 '18 at 08:20
  • Hi Hassen, Thanks for your quick reply. I have tried AUTO_ACKNOWLEDGE mode and it did not resolve the problem. – Roger Feb 16 '18 at 19:11
  • We use the Spring JMS Template for publishing the messages. When I monitor the ActiveMQ console, the queue clearly shows that the messages are stuck. If I publish 100 messages it shows 99 messages were consumes and 1 stuck. – Roger Feb 16 '18 at 19:13
  • Regarding logs, there are no exceptions in the logfile. What bothers me is, even if one of the consumers did not acknowledged and there are free consumers available why the messages are not dequeued and delivered? Here is the publishing code. – Roger Feb 16 '18 at 19:15
  • Please update your question with code, in comments it is unreadable – Hassen Bennour Feb 17 '18 at 11:27
  • 1
    try by replacing concurrentConsumers by concurrency="5-60" , receiveTimeout value="1000" and "queuePrefetch" value="1" – Hassen Bennour Feb 19 '18 at 09:03

0 Answers0