web-dev-qa-db-ja.com

Webアプリケーションからメッセージキューを聞く方法は? (Tomcat、ActiveMQ)

ApacheTomcatで実行されるWebアプリケーションを喜んで改善しています。メッセージを送受信するために、ActiveMQJMSサーバーが追加されました。

私はすでにメッセージを送受信できますが、受信者側で助けが必要です。

Webアプリは、メッセージを受信するために1つのキューを継続的にリッスンする必要がありますか?

新しいメッセージが到着し、サーバーはそれらに基づいて動作する必要があります。例:DBにデータを追加するか、メッセージを送り返します。

すでにメッセージを送信できます。これはコードです。

ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = factory.createConnection();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("clientQueue");
MessageProducer publisher = session.createProducer(queue);
connection.start();

Message message = null;
message = session.createTextMessage("Text Message");
publisher.send(message);

リクエスト後、すでにメッセージを受信できます(クリック;-))

connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("serverQueue");
consumer = session.createConsumer(destination);

while (true) {
    Message message = consumer.receive(300000);
    //Do message stuff
}

Webアプリケーションにキューを継続的にリッスンさせるにはどうすればよいですか?推奨される方法は何ですか?

すべての助けに心から感謝します。ありがとう。

編集-ソリューション

DaveHからの提案による現在の実用的なソリューション

メッセージを継続的にリッスンするためにServletContextListenerを追加しました。

web.xml

<listener>
    <listener-class>com.test.JMSContextListener</listener-class>
</listener>

リステレン:

public class JMSContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        Thread thread = new Thread(new JMSConnector());
        thread.start();
    }

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        //Nothing
    }
}

接続:

public class JMSConnector implements Runnable {
    public void run() {
        try {
            Context context = new InitialContext();
            QueueConnectionFactory factory = (QueueConnectionFactory) context.lookup("Java:comp/env/jms/ConnectionFactory");            
            Connection connection = factory.createConnection();
            Queue queue = (javax.jms.Queue) context.lookup("Java:comp/env/jms/serverQueue");
            Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);

            MessageConsumer consumer = session.createConsumer(queue);

            //This MessageListener will do stuff with the message
            MessageListenerImpl messageListener = new MessageListenerImpl();
            consumer.setMessageListener(messageListener);
            connection.start();

            // Start connection or nothing will happen!!!
            connection.start();
        } catch (JMSException ex) {
            //TODO
        } catch (NamingException ex) {
            //TODO
        }
    }
}

これは推奨される方法ですか、それとも改善する必要がありますか?

すべての助けに心から感謝します。ありがとう。

8
Dimitri Dewaele

キューからのメッセージをすでに消費できるコードがある場合(そうしているように見えます)、問題はそのコードをどのように実行するかにあると思います。

フレームワークを使用していないようです。そのため、キューからメッセージを取得できるコードを取得して、アプリケーションサーバーの別のスレッドで実行することをお勧めします。そのスレッドをアプリケーションサーバーの起動時に起動し、アプリケーションサーバーが終了するときにそれ自体を整理します。

アプリサーバーの起動時にスレッドを開始する最も簡単な方法は、ServletContextListenerを導入することです(例 ここ 。)コンテキストリスナーで、キューリスニングコードを別のスレッドで開始します。

編集:この提案されたソリューションを使用し、上記のコードを質問に追加しました。

4
DaveH

私はwebApplicationspring-mvcとJMSテンプレートでactiveMQを次のアプローチで使用しています。

<bean id="connectionFactory" class="org.Apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL">
        <value>tcp://localhost:61616</value>
    </property>
</bean>

<bean id="messageSender" class="xyz.MessageSender"/>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="defaultDestination" ref="destination" />
</bean>
<bean id="destination" class="org.Apache.activemq.command.ActiveMQQueue">
    <constructor-arg value="REQUEST_QUEUE" />
</bean> 
<bean id="messageListener" class="xtz.MessageListener">
    <property name="listenerId" value="1" />
</bean> 

<jms:listener-container connection-factory="connectionFactory">     
    <jms:listener destination="RESPONSE_QUEUE" ref="messageListener" method="messageReceived" />
</jms:listener-container>

以下に示すSenderおよびListenerクラスの実装。

public class MessageSender 
{

    @Autowired
    private JmsTemplate jmsTemplate;

    public void sendMessage()
    {

        jmsTemplate.send(new MessageCreator()
        {
            public Message createMessage(Session session) throws JMSException
            {
                MapMessage message = session.createMapMessage();
                message.setString("messageType", XXX);
                message.setString("jsonMessage", XXXX);
                return message;
            }
        });
    }
}


public class MessageListener
{
    private int listenerId;

    @Override
    public void messageReceived(Map<String, Object> message) throws Exception
    {        
        //put your logic here 
    }

    public int getListenerId()
    {
        return listenerId;
    }

    public void setListenerId(int listenerId)
    {
        this.listenerId = listenerId;
    }
}
1
ManojP

私は春を使って待ち行列を聞いています。リスナーの定義は次のようになります。

<jms:listener-container connection-factory="jmsConnectionFactoryLocal">
            <jms:listener destination="QUEUE_NAME" ref="channelManagerSimulatorDefault"/>
</jms:listener-container>

jmsConnectionFactoryLocalは、MQに従って作成する必要があります。私の場合はIBMWebsphereMQだったので、jmsConnectionFactoryLocalの定義は次のようになります。

<bean id="mqConnectionFactoryLocal" class="com.ibm.mq.jms.MQQueueConnectionFactory">
       <property name="hostName">
         <value>THE_MQ_SERVER_IP</value>
       </property>
       <property name="port">
          <value>MQ_PORT</value>
       </property>
       <property name="queueManager">
          <value>QUEUE_MANAGER_NAME</value>
       </property>
       <property name="transportType">
          <value>1</value>
       </property>
    </bean>
    <bean id="jmsConnectionFactoryLocal" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
       <property name="targetConnectionFactory" ref="mqConnectionFactoryLocal"/>
       <property name="username" value="USER_NAME"/>
       <property name="password" value="PASSWORD"/>
    </bean>

activeMQに適切なConnectionFactory実装を見つけて、それを使用する必要があります。リスナーとjmsConnectionFactoryは同じであり、MQプロバイダーから独立しています。

1
Hossein
  1. TomcatでJMSキューを構成します: https://martinsdeveloperworld.wordpress.com/2013/03/03/Apache-activemq-and-Tomcat/

  2. Activemq-all-5.xx.jarを$ Tomcat_HOME/libに配置します

  3. @WebListenerで開始されたスレッドでサイクルでリッスンします:

  4. ContextDestroyed時に停止します

    @WebListener
    @Slf4j
    public class JmsMailListener implements ServletContextListener {
        private Thread listenerThread = null;
        private QueueConnection connection;
    
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            try {
                InitialContext initCtx = new InitialContext();
                ActiveMQConnectionFactory connectionFactory =
                        (ActiveMQConnectionFactory) initCtx.lookup("Java:comp/env/jms/ConnectionFactory");
                connectionFactory.setTrustAllPackages(true);
                connection = connectionFactory.createQueueConnection();
                QueueSession queueSession = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
                Queue queue = (Queue) initCtx.lookup("Java:comp/env/jms/queue/MailQueue");
                QueueReceiver receiver = queueSession.createReceiver(queue);
                connection.start();
                log.info("Listen JMS messages ...");
                listenerThread = new Thread(() -> {
                    try {
                        while (!Thread.interrupted()) {
                            Message m = receiver.receive();
                            if (m instanceof ObjectMessage) {
                                ObjectMessage om = (ObjectMessage) m;
                                MyObject myObject = (MyObject) om.getObject();
                                log.info("Received MyObject {}", myObject);
                                ...
                            }
                        }
                    } catch (Exception e) {
                        log.error("Receiving messages failed: " + e.getMessage(), e);
                    }
                });
                listenerThread.start();
            } catch (Exception e) {
                log.error("JMS failed: " + e.getMessage(), e);
            }
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            if (connection != null) {
                try {
                    connection.close();
                } catch (JMSException ex) {
                    log.warn("Couldn't close JMSConnection: ", ex);
                }
            }
            if (listenerThread != null) {
                listenerThread.interrupt();
            }
        }
    }
    
1
GKislin