简体   繁体   English

在Java中动态创建异步消息队列

[英]Dynamically creating asynchronous message queues in Java

I need to create asynchronous message queues dynamically in Java. 我需要在Java中动态创建异步消息队列。 My use case is sending email via multiple SMTP servers: I need to enforce that emails to the same SMTP server are processes sequentially, but emails to different SMTP servers may be processed concurrently. 我的用例是通过多个SMTP服务器发送电子邮件:我需要按顺序强制执行相同SMTP服务器的电子邮件,但可以同时处理发送到不同SMTP服务器的电子邮件。 I've used JMS in the past, but as far as I can see it only allows for compile-time queue creation, whereas I need to create queues at runtime (one queue for each SMTP server). 我以前使用过JMS,但据我所知它只允许编译时队列创建,而我需要在运行时创建队列(每个SMTP服务器一个队列)。

Am I missing something regarding JMS or is there some other tool/proposal which I should have a look at? 我是否遗漏了有关JMS的内容,或者是否有其他工具/建议我应该查看一下?

I agree with Adam, the use case sounds like JMS is overhead. 我同意Adam,用例听起来像JMS是开销。 Java built-in functionality sufficient: 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);
        }

    }

}

JMS itself as a spec is rather silent on the issue. 作为规范的JMS本身在这个问题上相当沉默。 Most implementations allow you to do this, just not through JMS itself, but using their own API. 大多数实现允许您执行此操作,而不是通过JMS本身,而是使用自己的API。 But you won't be able to hook up something formal like an MDB to a dynamic queue. 但是,您无法将像MDB这样的正式内容连接到动态队列。 Rather you'll need to manage your own connections and listeners. 相反,您需要管理自己的连接和监听器。

The last time we looked at this in a WebSphere environment it was surprisingly difficult/impossible to create queues dynamically (temporary queues are too transient for you I think). 我们最后一次在WebSphere环境中查看它时,动态创建队列是非常困难/不可能的(我认为临时队列对你来说太短暂了)。 Although APIs for creating queues existed they required a server restart afterwards to become active. 虽然存在用于创建队列的API,但是之后需要重新启动服务器才能变为活动状态。 Then there's the MDB issue allused to. 然后就是MDB问题。

How about a dirty work-around based on the adage that all problems can be solved by an extra level of indirection, which assumes that the set of available printers is comparatively small. 如何通过额外的间接级别解决所有问题来解决所有问题的肮脏变通方法,这假设可用打印机的集合相对较小。

Create Queues Printer01 to Printer99 (or some smaller number). 创建队列Printer01到Printer99(或一些较小的数字)。 Have a "database" which maps queues to real printers. 有一个“队列”,将队列映射到真正的打印机。 As requests for printers come along you can add to the mapping table. 随着打印机的请求出现,您可以添加到映射表。 You might have some overhead of MDBs looking at queues that will never be used, but unless your pootential number of printers is vast maybe you can afford it? 您可能会有一些MDB查看永远不会使用的队列的开销,但除非你的潜在数量很大的打印机,否则你可以负担得起吗?

为每个SMTP服务器和限制队列使用者(MDB或消息侦听器)创建一个队列为1

我用activemq完成了这个 - 我当时实际上已经发布了一个问题,因为我有类似的问题(当时的JMS文档声明这不受支持)并且确信它得到了支持。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM