ActiveMQ的Destination高级特性

1.    Composite Destinations  组合目的地

组合队列Composite Destinations : 允许用一个虚拟的destination代表多个destinations,这样就可以通过composite destinations在一个操作中同时向多个queue/topic发送消息。
  有两种实现方式:
    第一种:在客户端编码实现
    第二种:在activemq.xml配置文件中实现

  • 第一种:在客户端编码实现

    在composite destinations中,多个destination之间采用","分隔。如下:这里有2个destination  "my-queue3"和"topic://topic-1",这个代表主题模式的topic-1

private static final String queueName = "my-queue3,topic://topic-1";

 

  默认是queue模式。

 

package cn.qlq.activemq;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * 生产消息
 */
public class MsgProducer {

    // 默认端口61616
    private static final String url = "tcp://localhost:61616/";
    private static final String queueName = "my-queue3,topic://topic-1";
    private static Session session = null;

    public static void main(String[] args) throws JMSException {
        // 1创建ConnectionFactory
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        // 2.由connectionFactory创建connection
        Connection connection = connectionFactory.createConnection();
        // 3.启动connection
        connection.start();
        // 4.创建Session===第一个参数是是否事务管理,第二个参数是应答模式
        session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
        // 5.创建Destination(Queue继承Queue)
        Queue destination = session.createQueue(queueName);

        TemporaryQueue temporaryQueue = session.createTemporaryQueue();

        // 6.创建生产者producer
        MessageProducer producer = session.createProducer(destination);
        for (int i = 0; i < 5; i++) {
            // 7.创建Message,有好多类型,这里用最简单的TextMessage
            TextMessage tms = session.createTextMessage("textMessage:" + i);

            // 设置附加属性
            tms.setStringProperty("str", "stringProperties" + i);
            tms.setJMSPriority(6);
            tms.setJMSReplyTo(temporaryQueue);

            // 8.生产者发送消息
            producer.send(tms);
        }

        // 9.提交事务
        session.commit();

        // 10.关闭connection
        session.close();
        connection.close();
    }

}

 

  结果会创建5条  my-queue3  队列消息 与 5条 主题模式   topic-1   消息。

  消费者正常消费即可,与队列模型的消息和主题模式的消息消费一样。

  • 第二种:在activemq.xml配置文件中实现
    <broker xmlns="http://activemq.apache.org/schema/core" brokerName="brokerName" dataDirectory="${activemq.data}">
    
        <destinationInterceptors>
           <virtualDestinationInterceptor>
              <virtualDestinations>
                     <compositeQueue name="comQueue">
                            <forwardTo>
                                   <queue physicalName="queue88" />
                                   <topic physicalName="topic88" />
                            </forwardTo>
                     </compositeQueue>
              </virtualDestinations>
           </virtualDestinationInterceptor>
        </destinationInterceptors>
    。。。

 

程序中向组合队列发送消息:

package cn.qlq.activemq;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * 生产消息
 */
public class MsgProducer {

    // 默认端口61616
    private static final String url = "tcp://localhost:61616/";
    private static final String queueName = "comQueue";
    private static Session session = null;

    public static void main(String[] args) throws JMSException {
        // 1创建ConnectionFactory
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        // 2.由connectionFactory创建connection
        Connection connection = connectionFactory.createConnection();
        // 3.启动connection
        connection.start();
        // 4.创建Session===第一个参数是是否事务管理,第二个参数是应答模式
        session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
        // 5.创建Destination(Queue继承Queue)
        Queue destination = session.createQueue(queueName);

        TemporaryQueue temporaryQueue = session.createTemporaryQueue();

        // 6.创建生产者producer
        MessageProducer producer = session.createProducer(destination);
        for (int i = 0; i < 5; i++) {
            // 7.创建Message,有好多类型,这里用最简单的TextMessage
            TextMessage tms = session.createTextMessage("textMessage:" + i);

            // 设置附加属性
            tms.setStringProperty("str", "stringProperties" + i);
            tms.setJMSPriority(6);
            tms.setJMSReplyTo(temporaryQueue);

            // 8.生产者发送消息
            producer.send(tms);
        }

        // 9.提交事务
        session.commit();

        // 10.关闭connection
        session.close();
        connection.close();
    }

}

结果:

  queue88产生五条消息:

 

   topic88生产五条消息:

 

 

 2 .Configure Startup Destinations--启动创建队列和主题,只是没有消息

  在启动ActiveMQ的时候如果需要创建Destination的话,可以在activemq.xml中配置:

    <broker xmlns="http://activemq.apache.org/schema/core" brokerName="brokerName" dataDirectory="${activemq.data}">
        <destinations>
              <queue physicalName="autoqueue" />
              <topic physicalName="autotopic" />
       </destinations>
    ...

 

 3.Delete Inactive Destinations---删除没有消息的队列或主题

  在ActiveMQ的queue在不使用之后,可以通过web控制台或者JMX方式来删除掉,当然,也可以通过配置,使得broker可以自动探测到无用的队列并删除掉,回收响应资源。

    <broker xmlns="http://activemq.apache.org/schema/core" brokerName="brokerName" dataDirectory="${activemq.data}" schedulePeriodForDestinationPurge="1000">
        <destinationPolicy>
              <policyMap>
                <policyEntries>
                     <policyEntry queue=">" gcInactiveDestinations="true" inactiveTimeoutBeforeGC="30000" />
                </policyEntries>
              </policyMap>
       </destinationPolicy>
    。。。

 说明:
  schedulePeriodForDestinationPurge: 设置多长时间检查一次,这里是1秒。
  inactiveTimoutBeforeGC: 设置当Destination为空后,多长时间被删除,这里是30秒。
  gcInactiveDestinations:设置删除掉不活动的队列,默认为false

 4.wildcars(通配符)

  Wildcars用来支持名字分层体系,它不是JMS规范的一部分,是ActiveMQ的扩展。

  ActiveMQ支持以下三种wildcars:
    .     用于作为路径上名字间的分隔符
    *    用于匹配路径上的任何名字
    >   用于递归地匹配任何以这个名字开始的destination

 

5.  Destination 选项

这个是给消费者在JMS规范之外添加的功能特性,通过在队列名称后面使用类似url的语法添加多个选项。包括:
1 consumer.perfetchSize,消费者持有的未确认的最大消费数量
2 consumer.maximumPendingMessageLimit: 用来控制非持久化的topic在存在慢消费者的情况下,丢弃的数量,默认为0
3 consumer.noLocal: 默认false
4 consumer.dispatchAsync: 是否异步分发,默认true
5 consumer.retroactive: 是否为回溯消费者,默认false
6 consumer.selector: JMS的selector,默认null
7 consumer.exclusive: 是否为独占消费者,默认false
8 consumer.priority:设置消费者的优先级,默认0

 使用示例:

Queue queue = new ActiveMQQueue("TEST.QUEUE?consumer.dispatchAsync=
false&consumer.perfetchSize=10");
Consumer consumer = session.createConsumer(queue);

 

6.    虚拟destination用来创建逻辑destination,客户端可以通过它来生产和消费消息,它会把消息映射到物理destination.

ActiveMQ支持2种方式:
  1:虚拟主题(Virtual Topics)
  2:组合Destinations(Composite Destinations)

为什么使用虚拟主题?
  ActiveMQ只有在持久订阅才是持久化的。持久订阅时,每一个持久订阅者,都相当于一个queue的客户端,它会收取所有消息。这种情况下存在两个问题:
第一:同一应用内消费者端护在均衡的问题。也就是说一个应用程序内的持久化消息,不能使用对个消费者共同承担消息处理能力。因为每个消费者都会获取所有消息。因为每一个消费者都会获取所有信息。
Queue到时可以解决这个问题,但broker端又不能将消息发送到多个应用端,所以纪要发布订阅,又要让消费者分组,这个功能JMS本身是没有的
第二:同一应用内消费者端failover问题,由于只能使用单个的持久订阅者,如果这个订阅者出错,则应用就无法处理,系统的健壮性不高。

如何使用虚拟topic?
第一:对于消息发布者来说,就是一个正常的topic,名称以VirtualTopic.开始,比如VirtualTopic.Orders,代码示例如下:

Topicdestination = session.createTopic("VirtualTopic.Orders");

第二:对于消息接收端来说,是个队列,不同应用里使用不同的前缀作为队列名称,即可表明自己的身份即可实现消费端应用分组。
例如Consumer.A.VirtualTopic.Orders说明它是名称为A的消费端,同理Consumer.B VirtualTopic.Orders说明是一名称为B的消费端。可以在同一个应用中使用多个消费者消费这个队列
又因为不同应用使用的topic名称不一样,前缀不同,所以不同应用中都可以接受到全部消息。每一个客户端相当于一个持久订阅者,而且这个客户端可以使用多个消费者共同来承担消费任务。

代码示例:

Destination dest = session.createQueue("Consumer.A.VirtualTopic.Orders");

 

生产者代码:

package cn.qlq.activemq.topic;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

public class MsgProducer {

    private static final String url = "tcp://127.0.0.1:61616";
    private static final String topicName = "VirtualTopic.Orders";

    public static void main(String[] args) throws JMSException {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);

        Connection connection = connectionFactory.createConnection();

        connection.start();

        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        Destination destination = session.createTopic(topicName);

        MessageProducer producer = session.createProducer(destination);
        for (int i = 0; i < 10; i++) {

            TextMessage tms = session.createTextMessage("textMessage:" + i);

            producer.send(tms);

            System.out.println("send:" + tms.getText());
        }
        connection.close();
    }

}

 

消费者代码:

package cn.qlq.activemq;

import java.util.Enumeration;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * 消费消息
 * 
 * @author QiaoLiQiang
 * @time 2018年9月18日下午11:26:41
 */
public class MsgConsumer {

    // 默认端口61616
    private static final String url = "tcp://localhost:61616/";
    private static final String queueName = "Consumer.A.VirtualTopic.Orders";

    public static void main(String[] args) throws JMSException {
        // 1创建ConnectionFactory
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        // 2.由connectionFactory创建connection
        Connection connection = connectionFactory.createConnection();
        Enumeration jmsxPropertyNames = connection.getMetaData().getJMSXPropertyNames();
        while (jmsxPropertyNames.hasMoreElements()) {
            String nextElement = (String) jmsxPropertyNames.nextElement();
            System.out.println("JMSX name ===" + nextElement);
        }
        // 3.启动connection
        connection.start();
        // 4.创建Session===第一个参数是是否事务管理,第二个参数是应答模式
        Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
        // 5.创建Destination(Queue继承Queue)
        Queue destination = session.createQueue(queueName);
        // 6.创建消费者consumer
        MessageConsumer consumer = session.createConsumer(destination);

        int i = 0;
        while (i < 5) {
            TextMessage textMessage = (TextMessage) consumer.receive();
            System.out.println("接收消息:" + textMessage.getText() + ";属性" + textMessage.getStringProperty("str"));
            i++;

            if (i == 5) {// 确保消费完所有的消息再进行确认
                textMessage.acknowledge();
            }
        }

        // 提交事务,进行确认收到消息
        session.commit();

        session.close();
        connection.close();
    }
}

 

其实把消费者队列化了。

修改虚拟主题的前缀:

默认前缀是VirtualTopic.>

自定义消费虚拟地址默认格式:Consumer.*.VirtualTopic.>

修改配置:

<broker xmlns="http://activemq.apache.org/schema/core">
       <destinationInterceptors>
              <virtualDestinationInterceptor>
                     <virtualDestinations>
                            <virtualTopic name=">" prefix="VirtualTopicConsumers.*." selectorAware="false" />
                     </virtualDestinations>
              </virtualDestinationInterceptor>
       </destinationInterceptors>
</broker>

 

posted @ 2019-03-22 22:20  QiaoZhi  阅读(2518)  评论(0编辑  收藏  举报