7

Java で非同期メッセージ キューを動的に作成する必要があります。私の使用例は、複数の SMTP サーバー経由で電子メールを送信することです。同じ SMTP サーバーへの電子メールは順次処理されるようにする必要がありますが、異なる SMTP サーバーへの電子メールは同時に処理される可能性があります。過去に JMS を使用したことがありますが、実行時にキューを作成する必要があるのに対し (SMTP サーバーごとに 1 つのキュー)、コンパイル時のキューの作成しか許可されていません。

JMS に関して何か不足していますか、それとも他のツールや提案を確認する必要がありますか?

4

5 に答える 5

6

私は Adam に同意します。ユース ケースは JMS がオーバーヘッドのように聞こえます。Java 組み込み機能で十分:

package de.mhaller;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;

import org.junit.Assert;
import org.junit.Test;

public class Mailer {

    @Test
    public void testMailer() throws Exception {
        ExecutorService executor = Executors.newCachedThreadPool();
        ArrayList<Mail> log = new ArrayList<Mail>();
        LinkedBlockingDeque<Mail> incoming = new LinkedBlockingDeque<Mail>();

        // TODO: Put mails to be sent into the incoming queue
        incoming.offer(new Mail("foo1@localhost", "localhost"));
        incoming.offer(new Mail("foo2@otherhost", "otherhost"));
        incoming.offer(new Mail("foo3@otherhost", "otherhost"));
        incoming.offer(new Mail("foo4@localhost", "localhost"));

        Map<Mailserver, Queue<Mail>> queues = new HashMap<Mailserver, Queue<Mail>>();
        while (!incoming.isEmpty()) {
            Mail mail = incoming.pollFirst();
            Mailserver mailserver = findMailserver(mail);
            if (!queues.containsKey(mailserver)) {
                ArrayDeque<Mail> serverQueue = new ArrayDeque<Mail>();
                queues.put(mailserver, serverQueue);
                executor.execute(new SendMail(mailserver, serverQueue));
            }
            Queue<Mail> slot = queues.get(mailserver);
            slot.offer(mail);
        }

        assertMailSentWithCorrectServer(log);
    }

    private void assertMailSentWithCorrectServer(ArrayList<Mail> log) {
        for (Mail mail : log) {
            if (!mail.server.equals(mail.sentBy.mailserver)) {
                Assert.fail("Mail sent by wrong server: " + mail);
            }
        }
    }

    private Mailserver findMailserver(Mail mail) {
        // TODO: Your lookup logic which server to use
        return new Mailserver(mail.server);
    }

    private static class Mail {
        String recipient;
        String server;
        SendMail sentBy;

        public Mail(String recipient, String server) {
            this.recipient = recipient;
            this.server = server;
        }

        @Override
        public String toString() {
            return "mail for " + recipient;
        }
    }

    public static class SendMail implements Runnable {

        private final Deque<Mail> queue;
        private final Mailserver mailserver;

        public SendMail(Mailserver mailserver, Deque<Mail> queue) {
            this.mailserver = mailserver;
            this.queue = queue;
        }

        @Override
        public void run() {
            while (!queue.isEmpty()) {
                Mail mail = queue.pollFirst();
                // TODO: Use SMTP to send the mail via mailserver
                System.out.println(this + " sent " + mail + " via " + mailserver);
                mail.sentBy = this;
            }
        }

    }

    public static class Mailserver {
        String hostname;

        public Mailserver(String hostname) {
            this.hostname = hostname;
        }

        @Override
        public String toString() {
            return hostname;
        }

        @Override
        public int hashCode() {
            return hostname.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            return hostname.equals(((Mailserver) obj).hostname);
        }

    }

}
于 2009-10-31T22:06:10.637 に答える
1

仕様としての JMS 自体は、この問題についてはかなり沈黙しています。ほとんどの実装では、JMS 自体ではなく、独自の API を使用してこれを行うことができます。ただし、MDB のような正式なものを動的キューに接続することはできません。むしろ、独自の接続とリスナーを管理する必要があります。

于 2009-10-29T07:41:49.103 に答える
1

WebSphere 環境でこれを最後に見たとき、キューを動的に作成することは驚くほど困難/不可能でした (一時的なキューは一時的すぎると思います)。キューを作成するための API は存在していましたが、後でサーバーを再起動してアクティブにする必要がありました。次に、MDB の問題があります。

すべての問題は余分なレベルの間接化によって解決できるという格言に基づいた汚い回避策はどうでしょうか。これは、使用可能なプリンターのセットが比較的小さいことを前提としています。

キュー Printer01 から Printer99 (またはいくつかの小さい番号) を作成します。キューを実際のプリンターにマップする「データベース」を用意します。プリンターのリクエストが来たら、マッピング テーブルに追加できます。決して使用されないキューを参照する MDB のオーバーヘッドが発生する可能性がありますが、プリンターの潜在的な数が膨大でない限り、余裕があるのではないでしょうか?

于 2009-10-29T07:52:33.887 に答える
0

SMTP サーバーごとにキューを作成し、キュー コンシューマー (MDB またはメッセージ リスナー) を 1 に制限します。

于 2009-10-30T12:29:32.933 に答える
0

私はこれをactivemqで行いました-同様の懸念があり(当時のJMSドキュメントではこれはサポートされていないと述べられていました)、サポートされていることが保証されたため、実際に当時これについて質問を投稿しました。

于 2009-10-30T17:20:27.280 に答える