web-dev-qa-db-ja.com

JMS-1つから複数のコンシューマーへ

メッセージを生成し、JMSキューを介してその固有のコンシューマーに送信するJMSクライアントがあります。

私が欲しいのは、それらのメッセージを受け取る複数の消費者です。最初に頭に浮かぶのは、キューをトピックに変換することです。そのため、現在のユーザーと新しいユーザーがサブスクライブして、同じメッセージをすべてのユーザーに配信できます。

これには明らかに、プロデューサー側とコンシューマー側の両方で現在のクライアントコードを変更する必要があります。

また、既存のコンシューマーを変更する必要がないように、2番目のキューの作成など、他のオプションも確認したいと思います。このアプローチには、1つではなく2つの異なるキュー間で負荷を分散する(間違っている場合は修正する)などの利点があると思います。これは、パフォーマンスにプラスの影響を与える可能性があります。

私はこれらのオプションとあなたが見るかもしれない短所/長所について助言を得たいと思います。どんなフィードバックでも大歓迎です。

あなたが述べたようにあなたはいくつかのオプションがあります。

同じ効果を得るためにトピックに変換する場合、コンシューマーを永続的なコンシューマーにする必要があります。キューが提供するものの1つは、コンシューマーが生きていない場合の持続性です。これは、使用しているMQシステムによって異なります。

キューを使い続ける場合は、各コンシューマ用のキューと、元のキューをリッスンするディスパッチャを作成します。

Producer -> Queue_Original <- Dispatcher -> Queue_Consumer_1 <- Consumer_1
                                         -> Queue_Consumer_2 <- Consumer_2
                                         -> Queue_Consumer_3 <- Consumer_3

トピックの長所

  • 新しいコンシューマーを動的に追加するのが簡単になります。すべてのコンシューマーは、何もしなくても新しいメッセージを受け取ります。
  • ラウンドロビントピックを作成して、Consumer_1がメッセージを取得し、次にConsumer_2、次にConsumer_3と取得するようにできます。
  • コンシューマーは、キューにクエリを送ってリアクティブにする代わりに、新しいメッセージをプッシュできます。

トピックの短所

  • ブローカーがこの構成をサポートしない限り、メッセージは永続的ではありません。コンシューマがオフラインになって戻ってきた場合、永続的なコンシューマが設定されていないと、メッセージを見逃している可能性があります。
  • Consumer_1とConsumer_2はメッセージを受信できますが、Consumer_3は受信できません。 Dispatcherとキューを使用すると、DispatcherはConsumer_3のキューにメッセージを書き込むことができません。

キューの長所

  • メッセージは、コンシューマーが削除するまで持続します
  • ディスパッチャーは、メッセージをそれぞれのコンシューマーキューに入れないことにより、どのコンシューマーがどのメッセージを取得するかをフィルターに掛けることができます。ただし、これはフィルターを介してトピックで行うことができます。

キューの短所

  • 複数のコンシューマをサポートするには、追加のキューを作成する必要があります。動的な環境では、これは効率的ではありません。

メッセージングシステムを開発するときは、トピックの方が最も強力ですが、すでにキューを使用しているので、システムの動作を変更してトピックを実装する必要があります。

複数のコンシューマを持つキューシステムの設計と実装

Producer -> Queue_Original <- Dispatcher -> Queue_Consumer_1 <- Consumer_1
                                         -> Queue_Consumer_2 <- Consumer_2
                                         -> Queue_Consumer_3 <- Consumer_3

ソース

問題の例外処理、接続への再接続、接続が失われた場合のキューなど、注意する必要のある他の事項があることを覚えておいてください。説明。

実際のシステムでは、最初の例外ではおそらく終了しません。システムができる限り最善の動作を続け、エラーをログに記録できるようにします。このコードでは、単一のコンシューマキューへのメッセージの送信が失敗した場合、ディスパッチャ全体が停止します。

Dispatcher.Java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package stackoverflow_4615895;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.Session;

public class Dispatcher {

    private static long QUEUE_WAIT_TIME = 1000;
    private boolean mStop = false;
    private QueueConnectionFactory mFactory;
    private String mSourceQueueName;
    private String[] mConsumerQueueNames;

    /**
     * Create a dispatcher
     * @param factory
     *      The QueueConnectionFactory in which new connections, session, and consumers
     *      will be created. This is needed to ensure the connection is associated
     *      with the correct thread.
     * @param source
     *
     * @param consumerQueues
     */
    public Dispatcher(
        QueueConnectionFactory factory, 
        String sourceQueue, 
        String[] consumerQueues) {

        mFactory = factory;
        mSourceQueueName = sourceQueue;
        mConsumerQueueNames = consumerQueues;
    }

    public void start() {
        Thread thread = new Thread(new Runnable() {

            public void run() {
                Dispatcher.this.run();
            }
        });
        thread.setName("Queue Dispatcher");
        thread.start();
    }

    public void stop() {
        mStop = true;
    }

    private void run() {

        QueueConnection connection = null;
        MessageProducer producer = null;
        MessageConsumer consumer = null;
        QueueSession session = null;
        try {
            // Setup connection and queues for receiving the messages
            connection = mFactory.createQueueConnection();
            session = connection.createQueueSession(false, Session.DUPS_OK_ACKNOWLEDGE);
            Queue sourceQueue = session.createQueue(mSourceQueueName);
            consumer = session.createConsumer(sourceQueue);

            // Create a null producer allowing us to send messages
            // to any queue.
            producer = session.createProducer(null);

            // Create the destination queues based on the consumer names we
            // were given.
            Queue[] destinationQueues = new Queue[mConsumerQueueNames.length];
            for (int index = 0; index < mConsumerQueueNames.length; ++index) {
                destinationQueues[index] = session.createQueue(mConsumerQueueNames[index]);
            }

            connection.start();

            while (!mStop) {

                // Only wait QUEUE_WAIT_TIME in order to give
                // the dispatcher a chance to see if it should
                // quit
                Message m = consumer.receive(QUEUE_WAIT_TIME);
                if (m == null) {
                    continue;
                }

                // Take the message we received and put
                // it in each of the consumers destination
                // queues for them to process
                for (Queue q : destinationQueues) {
                    producer.send(q, m);
                }
            }

        } catch (JMSException ex) {
            // Do wonderful things here 
        } finally {
            if (producer != null) {
                try {
                    producer.close();
                } catch (JMSException ex) {
                }
            }
            if (consumer != null) {
                try {
                    consumer.close();
                } catch (JMSException ex) {
                }
            }
            if (session != null) {
                try {
                    session.close();
                } catch (JMSException ex) {
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (JMSException ex) {
                }
            }
        }
    }
}

Main.Java

    QueueConnectionFactory factory = ...;

    Dispatcher dispatcher =
            new Dispatcher(
            factory,
            "Queue_Original",
            new String[]{
                "Consumer_Queue_1",
                "Consumer_Queue_2",
                "Consumer_Queue_3"});
    dispatcher.start();
49

コードを変更する必要はありません。それはあなたがそれを書いた方法に依存します。

たとえば、コードがMessageProducerではなくQueueSenderを使用してメッセージを送信する場合、キューだけでなくトピックでも機能します。同様に、MessageConsumerではなくQueueReceiverを使用した場合。

基本的に、JMSアプリケーションでは、MessageProducerMessageConsumerDestinationなどの非特定のインターフェイスを使用してJMSシステムとやり取りすることをお勧めします。場合、それは構成の「単なる」問題です。

4
skaffman