如何在代码中创建 Apache Artemis 队列并将它们与 JMS 一起使用?

How to create Apache Artemis queues in code and use them with JMS?

我正在将 JavaLite Async 从 Artemis 2.3.0 迁移到 2.11.0 版本。 JavaLite Async 不使用任何文件基础配置,而是依赖于代码。

在 v 2.3.0 和 2.11.0 之间,JMS 管理 API 现在是 gone/deprecated,我们鼓励使用核心管理 API。

不幸的是我找不到方法:

  1. 以编程方式创建队列
  2. 查找使用JNDI的队列,使用JMS收发消息。

这是一个示例(为简洁起见,保留导入):

class QueueLookup {
    private static final String LOCATION = "./target/artemis";

    private static EmbeddedActiveMQ server;

    public static void main(String[] args) throws Exception {
        try{
            Configuration configuration = new ConfigurationImpl()
                    .setPersistenceEnabled(true)
                    .setBindingsDirectory(LOCATION + "/bindings")
                    .setJournalDirectory(LOCATION + "/journal")
                    .setLargeMessagesDirectory(LOCATION + "/largemessages")
                    .setPagingDirectory(LOCATION + "/paging")
                    .setSecurityEnabled(false)
                    .addAcceptorConfiguration("invm", "vm://0")
                    .setJournalBufferTimeout_AIO(100)
                    .setJournalBufferTimeout_NIO(100)
                    .setJournalType(JournalType.NIO)
                    .setMaxDiskUsage(90);


            //the following three lines have no effect
            CoreQueueConfiguration coreQueueConfiguration = new CoreQueueConfiguration();
            coreQueueConfiguration.setName("Queue123").setDurable(true);
            configuration.addQueueConfiguration(coreQueueConfiguration);


            server = new EmbeddedActiveMQ();
            server.setConfiguration(configuration);
            server.start();


            TransportConfiguration transportConfiguration = new TransportConfiguration(InVMConnectorFactory.class.getName());
            ConnectionFactory connectionFactory = ActiveMQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, transportConfiguration);

            Hashtable<String, String> jndi = new Hashtable<>();
            jndi.put("java.naming.factory.initial", "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory");
            jndi.put("connectionFactory.ConnectionFactory", "vm://0");
            //# queue.[jndiName] = [physicalName]
            jndi.put("queue.queue/Queue123", "Queue123");

            InitialContext initialContext = new InitialContext(jndi);
            Queue jmsQueue = (Queue) initialContext.lookup("queue/Queue123");

            try (Connection connection = connectionFactory.createConnection()) {
                try(Session jmsSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE)){
                    MessageProducer producer = jmsSession.createProducer(jmsQueue);
                    connection.start();
                    TextMessage message = jmsSession.createTextMessage("Hello, Artemis!");
                    producer.send(message);
                    System.out.println("Message sent: " + message.getText());
                }
            } catch (Exception ex){
                ex.printStackTrace();
            }

        }finally {
            server.stop();
        }
    }
}

这些行无效:

CoreQueueConfiguration coreQueueConfiguration = new CoreQueueConfiguration();
coreQueueConfiguration.setName("Queue123").setDurable(true);
configuration.addQueueConfiguration(coreQueueConfiguration);

但是,如果我删除这一行:

jndi.put("queue.queue/Queue123", "Queue123");

那么 JNDI 找不到队列。

从表面上看,我似乎可以 "create" 通过将它们的名称添加到 JNDI 来排队:

jndi.put("queue.queue/Queue123", "Queue123");

然而,这只部分有效,队列似乎存在于 发送和接收消息,而 QueueControlQueueBrtowser 都找不到它。

谁能解释一下我如何在代码中做到这一点(没有XML配置):

  1. 创建一个队列并传递所有必要的参数(持久等)
  2. 使用 JNDI 查找此队列
  3. 使用 QueueControl 控制此队列
  4. 使用 QueueBrowser 浏览此队列。

可以在此处找到带有 GUI 版本的完整示例:https://github.com/ipolevoy/artemis-sanbox/blob/master/src/main/java/examples/

非常感谢任何帮助!

这里似乎存在一些误解...

首先,您的 CoreQueueConfiguration 具有 "no effect" 的原因是它无效。它无效,因为您没有指定队列将绑定到的地址的名称。当代理出现表明配置无效时,它应该记录类似这样的内容:

WARN  [org.apache.activemq.artemis.core.server] AMQ222275: Failed to deploy queue <queue name>: null

JMS-to-core mapping documentation所述,一个JMS队列是一个核心地址,一个anycast核心队列同名。因此,您应该改用这样的配置:

coreQueueConfiguration.setAddress("Queue123").setName("Queue123").setDurable(true).setRoutingType(org.apache.activemq.artemis.api.core.RoutingType.ANYCAST);

我的猜测是部分工作正常,因为默认情况下核心 JMS 客户端将自动创建它需要的目的地,因此无论您在 JNDI 属性中配置什么,都将自动创建,而不管 CoreQueueConfiguration 您'指定。如果您不想自动创建 JMS 目标所需的底层地址和队列,那么您应该使用相应的地址设置禁用它。还有一些设置可以在不再使用地址和队列时自动删除它们(即当 message-count = 0 和 counsumer-count = 0 时),您可能也想禁用这些设置。这是一个例子:

server.getAddressSettingsRepository().addMatch("#", new AddressSettings()
   .setAutoCreateQueues(false)
   .setAutoDeleteQueues(false)
   .setAutoCreateAddresses(false)
   .setAutoDeleteAddresses(false));

其次,JNDI配置详见this bit of documentation:

JMS destinations are also typically looked up via JNDI. As with connection factories, destinations can be configured using special properties in the JNDI context environment. The property name should follow the pattern: queue.<jndi-binding> or topic.<jndi-binding>. The property value should be the name of the queue hosted by the Apache ActiveMQ Artemis server.

这解释了为什么需要这一行:

jndi.put("queue.queue/Queue123", "Queue123");

The documentation 还指出:

It is also possible to look-up JMS destinations which haven't been configured explicitly in the JNDI context environment. This is possible using dynamicQueues/ or dynamicTopics/ in the look-up string. For example, if the client wanted to look-up the aforementioned "OrderQueue" it could do so simply by using the string "dynamicQueues/OrderQueue". Note, the text that follows dynamicQueues/ or dynamicTopics/ must correspond exactly to the name of the destination on the server.

这意味着您可以省略上述行并改用这种查找:

Queue jmsQueue = (Queue) initialContext.lookup("dynamicQueues/Queue123");

也就是说,不清楚为什么要在这里使用 JNDI 而不是简单地使用 JMS API 实例化目标。您可以通过删除所有 JNDI 代码来显着简化该代码,例如:

ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://0");

try (Connection connection = connectionFactory.createConnection(); 
     Session jmsSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE)) {
   MessageProducer producer = jmsSession.createProducer(jmsSession.createQueue("Queue123"));
   connection.start();
   TextMessage message = jmsSession.createTextMessage("Hello, Artemis!");
   producer.send(message);
   System.out.println("Message sent: " + message.getText());
} catch (Exception ex) {
   ex.printStackTrace();
}

要获得队列的控制权,请使用如下内容:

QueueControl coreQueueControl = (QueueControl) server.getManagementService().getResource(org.apache.activemq.artemis.api.core.management.ResourceNames.QUEUE + "Queue123");

要浏览队列,您可以使用 javax.jms.QueueBrowser。网上有很多这方面的教程。

最后,在我们添加了对 AMQP、MQTT 和 OpenWire 的支持后,我们在 2.0.0 版发布之前做出了弃用 JMS 配置和管理位的决定。在这一点上,将配置和管理简化为核心资源(即地址、队列和路由类型)是有意义的。拥有所有核心内容 加上 相应的 JMS 配置和管理肯定会给用户带来困惑,并且也很难维护。我们花了相当多的时间更新文档来解释所有内容如何从各种支持的协议和 APIs 映射到核心资源。 This document 提供了您可能会觉得有用的概述。

其他详细信息:

配置队列:

CoreQueueConfiguration coreQueueConfiguration = new CoreQueueConfiguration();
coreQueueConfiguration.setAddress(QUEUE_NAME).setName(QUEUE_NAME).setDurable(true).setRoutingType(RoutingType.ANYCAST);
configuration.addQueueConfiguration(coreQueueConfiguration);

队列名称注册方式:

jndi.put("queue.queue/" + QUEUE_NAME, QUEUE_NAME);

代理配置方式:

server.getActiveMQServer().getAddressSettingsRepository().addMatch("#", new AddressSettings()
                .setAutoCreateQueues(false)
                .setAutoCreateAddresses(false)
                .setAutoDeleteQueues(false)
                .setAutoDeleteAddresses(false));

一个完整的工作示例:https://github.com/ipolevoy/artemis-sanbox/blob/7e2194788ec4e825d4219f06ebbaa9b9195e2b30/src/main/java/examples/JMSExampleJNDI_GUI.java#L23