Установите timeOut для сеанса / транзакции в DefaultMessageListenerContainer - PullRequest
0 голосов
/ 18 апреля 2019

Я хочу получать порцию сообщений из очереди в течение некоторого времени (например, 300 миллисекунд после получения 1-го сообщения) с использованием DefaultMessageListenerConatiner Of Spring (путем переопределения doReceiveAndExecute), как указано в ссылке .

Я могу сгруппировать сообщения размером моего пакета, т. Е. 20, когда в очереди слишком много сообщений, и я могу получить менее 20 сообщений, когда в очереди очень мало сообщений.

Выпуск:

Я вижу, что отправка сообщений в Listener занимает слишком много времени (иногда 1 секунда, иногда 2 секунды и более), даже когда очередь заполнена.

Когда я пытаюсь использовать DefaultMessageListenerConatiner как таковой для одновременного получения отдельных сообщений, я вижу, что сообщения принимаются с задержкой в ​​несколько миллисекунд (например, 1 миллисекунд или максимум от 30 до 60 миллисекунд)

Я не указывал транзакции или тайм-ауты и не связывал ни один менеджер транзакций.

Могут ли Спрингеры, пожалуйста, помочь мне найти, где можно указать timeOut или Как я могу вывести задержку?

BatchMessageListenerContainer:

package com.mypackage;

import javax.jms.Session;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;

import java.util.ArrayList;
import java.util.List;

import org.springframework.jms.listener.DefaultMessageListenerContainer;

import org.springframework.jms.connection.ConnectionFactoryUtils;
import org.springframework.jms.support.JmsUtils;
import org.springframework.transaction.TransactionStatus;

/**
 * Listener Container that allows batch consumption of messages. Works only with transacted sessions
 */
public class BatchMessageListenerContainer extends DefaultMessageListenerContainer {
  public static final int DEFAULT_BATCH_SIZE = 20;

  private int batchSize = DEFAULT_BATCH_SIZE;

  public BatchMessageListenerContainer() {
    super();
    setSessionTransacted(true);
  }

  /**
   * @return The batch size on this container
   */
  public int getBatchSize() {
    return batchSize;
  }

  /**
   * @param batchSize The batchSize of this container
   */
  public void setBatchSize(int batchSize) {
    this.batchSize = batchSize;
  }

  /**
   * The doReceiveAndExecute() method has to be overriden to support multiple-message receives.
   */
  @Override
  protected boolean doReceiveAndExecute(Object invoker, Session session, MessageConsumer consumer,
    TransactionStatus status) throws JMSException {

    Connection conToClose = null;
    MessageConsumer consumerToClose = null;
    Session sessionToClose = null;

    try {
      Session sessionToUse = session;
      MessageConsumer consumerToUse = consumer;

      if (sessionToUse == null) {
        Connection conToUse = null;
        if (sharedConnectionEnabled()) {
          conToUse = getSharedConnection();
        }
        else {
          conToUse = createConnection();
          conToClose = conToUse;
          conToUse.start();
        }
        sessionToUse = createSession(conToUse);
        sessionToClose = sessionToUse;
      }

      if (consumerToUse == null) {
        consumerToUse = createListenerConsumer(sessionToUse);
        consumerToClose = consumerToUse;
      }

      List<Message> messages = new ArrayList<Message>();

      int count = 0;
      Message message = null;
      // Attempt to receive messages with the consumer
      do {
        message = receiveMessage(consumerToUse);
        if (message != null) {
          messages.add(message);
        }
      }
      // Exit loop if no message was received in the time out specified, or
      // if the max batch size was met
      while ((message != null) && (++count < batchSize));

      if (messages.size() > 0) {
        // Only if messages were collected, notify the listener to consume the same.
        try {
          doExecuteListener(sessionToUse, messages);
          sessionToUse.commit();
        }
        catch (Throwable ex) {
          handleListenerException(ex);
          if (ex instanceof JMSException) {
            throw (JMSException) ex;
          }
        }
        return true;
      }

      // No message was received for the period of the timeout, return false.
      noMessageReceived(invoker, sessionToUse);
      return false;
    }
    finally {
      JmsUtils.closeMessageConsumer(consumerToClose);
      JmsUtils.closeSession(sessionToClose);
      ConnectionFactoryUtils.releaseConnection(conToClose, getConnectionFactory(), true);
    }
  }

  protected void doExecuteListener(Session session, List<Message> messages) throws JMSException {
    if (!isAcceptMessagesWhileStopping() && !isRunning()) {
      if (logger.isWarnEnabled()) {
        logger.warn("Rejecting received messages because of the listener container "
          + "having been stopped in the meantime: " + messages);
      }
      rollbackIfNecessary(session);
      throw new JMSException("Rejecting received messages as listener container is stopping");
    }

    @SuppressWarnings("unchecked")
    SessionAwareBatchMessageListener<Message> lsnr = (SessionAwareBatchMessageListener<Message>) getMessageListener();

    try {
      lsnr.onMessages(session, messages);
    }
    catch (JMSException ex) {
      rollbackOnExceptionIfNecessary(session, ex);
      throw ex;
    }
    catch (RuntimeException ex) {
      rollbackOnExceptionIfNecessary(session, ex);
      throw ex;
    }
    catch (Error err) {
      rollbackOnExceptionIfNecessary(session, err);
      throw err;
    }
  }

  @Override
  protected void checkMessageListener(Object messageListener) {
    if (!(messageListener instanceof SessionAwareBatchMessageListener<?>)) {
      throw new IllegalArgumentException("Message listener needs to be of type ["
        + SessionAwareBatchMessageListener.class.getName() + "]");
    }
  }

  @Override
  protected void validateConfiguration() {
    if (batchSize <= 0) {
      throw new IllegalArgumentException("Property batchSize must be a value greater than 0");
    }
  }

  public void setSessionTransacted(boolean transacted) {
    if (!transacted) {
      throw new IllegalArgumentException("Batch Listener requires a transacted Session");
    }
    super.setSessionTransacted(transacted);
  }
}

SessionAwareBatchMessageListener:

package com.mypackage;

import java.util.List;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;

public interface SessionAwareBatchMessageListener<M extends Message> {
  /**
   * Perform a batch action with the provided list of {@code messages}.
   * 
   * @param session JMS {@code Session} that received the messages
   * @param messages List of messages
   * @throws JMSException JMSException thrown if there is an error performing the operation.
   */
  public void onMessages(Session session, List<M> messages) throws JMSException;
}

Боб в applicationContext.xml:

<bean id="myMessageListener" class="org.mypackage.MyMessageListener">

<bean id="jmsContainer" class="com.mypackage.BatchMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destinationName" ref="queue"/>
    <property name="messageListener" ref="myMessageListener"/>
    <property name ="concurrentConsumers" value ="10"/>
    <property name ="maxConcurrentConsumers" value ="50"/>        
</bean>

MyMessageListner:

package org.mypackage;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;

import org.mypackage.service.MyService;

public class MyMessageListener implements SessionAwareBatchMessageListener<TextMessage> {

    @Autowired
    private MyService myService;

    @Override
    public void onMessage(Session session, List<TextMessage> messages) {
        try {
           for(TextMessage tm :messages)  {
                TextMessage textMessage = (TextMessage) message;
               // parse the message and add to list
            }
            //process list of Objects to DB
        } catch (JMSException e1) {
             e1.printStackTrace();
        }
    }
}

1 Ответ

0 голосов
/ 23 апреля 2019

я думаю, что время, потраченное перед отправкой сообщения потребителю, было вызвано вашим циклом while, потому что вы каждый раз ожидаете, что список будет полным, но этот заполнен только текущим потоком, поскольку он создается внутриметод doReceiveAndExecute!

  // Exit loop if no message was received in the time out specified, or
  // if the max batch size was met
  while ((message != null) && (++count < batchSize));

Может быть, это может сделать это хорошо:

...
     List<Message> messages = Collections.synchronizedList(new ArrayList<Message>());

     @Override
      protected boolean doReceiveAndExecute(Object invoker, Session session, MessageConsumer consumer,
        TransactionStatus status) throws JMSException {

        Connection conToClose = null;
        MessageConsumer consumerToClose = null;
        Session sessionToClose = null;

        try {
          Session sessionToUse = session;
          MessageConsumer consumerToUse = consumer;

          if (sessionToUse == null) {
            Connection conToUse = null;
            if (sharedConnectionEnabled()) {
              conToUse = getSharedConnection();
            }
            else {
              conToUse = createConnection();
              conToClose = conToUse;
              conToUse.start();
            }
            sessionToUse = createSession(conToUse);
            sessionToClose = sessionToUse;
          }

          if (consumerToUse == null) {
            consumerToUse = createListenerConsumer(sessionToUse);
            consumerToClose = consumerToUse;
          }


          Message message = null;
          // Attempt to receive messages with the consumer
          do {
            message = receiveMessage(consumerToUse);
            if (message != null) {
              messages.add(message);
            }
          }

          if (messages.size() >= batchSize)) {
            synchronized (messages) {
              // Only if messages were collected, notify the listener to consume the same.
              try {
                doExecuteListener(sessionToUse, messages);
                sessionToUse.commit();
                // clear the list!!
                messages.clear();
              }
              catch (Throwable ex) {
                handleListenerException(ex);
                if (ex instanceof JMSException) {
                  throw (JMSException) ex;
                }
              }
            }
            return true;
          }

          // No message was received for the period of the timeout, return false.
          noMessageReceived(invoker, sessionToUse);
          return false;
        }
        finally {
          JmsUtils.closeMessageConsumer(consumerToClose);
          JmsUtils.closeSession(sessionToClose);
          ConnectionFactoryUtils.releaseConnection(conToClose, getConnectionFactory(), true);
        }
      }
...