Active MQ 高级特性和用法(一):内嵌Active MQ、Active MQ的各种持久化方式、消息(Topic)的持久化订阅

Active MQ 高级特性和用法(一)内嵌Active MQ、Active MQ的各种持久化方式、消息(Topic)的持久化订阅

一、内嵌Active MQ

内嵌Active MQ就是在我们的Java程序里面用代码生成一个Active MQ的客户端,虽然我们自己用代码生成的Active MQ客户端没有真正的客户端的功能多,但是可能还是会用得到的。

    public static void main(String[] args) throws Exception {
        BrokerService brokerService = new BrokerService();
        brokerService.setBrokerName("EmbedMQ");
        brokerService.addConnector("tcp://localhost:62000");
        brokerService.setManagementContext(new ManagementContext());
        brokerService.start();
    }

上述几行代码便将一个简易的Active MQ客户端内嵌到我们的Java程序中了。我们可以编写一对生产者-消费者来测试一下,将Active MQ的端口号改为62000便可以使用我们内置的Active MQ客户端。

Active MQ消息存储的持久化

Active MQ持久化方式

    Active MQ的另一个问题就是只要是软件就有可能挂掉,挂掉不可怕,怕的是挂掉之后把信息给丢了,怎么办,可以进行消息的持久化,Active MQ提供了几种持久化方式:

  1. AMQ消息存储-基于文件的存储方式,它具有写入速度快和容易恢复的特点。消息存储在一个个文件中,文件的默认大小为32M,如果一条消息的大小超过了32M,那么这个值必须设置大一点。当一个存储文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。AMQ适用于Active MQ5.3之前的版本
  2. KahaDB消息存储-提供了容量的提升和恢复能力,是现在的默认存储方式;KahaDB是基于文件的本地数据库储存形式,虽然没有AMQ的速度快,但是它具有强扩展性,恢复的时间比AMQ短,从5.4版本之后KahaDB做为默认的持久化方式。
  3. JDBC消息存储-消息基于JDBC存储的;
  4. Memory消息存储-基于内存的消息存储,由于内存不属于持久化范畴。所以内存存储不在讨论范围内。

KahaDB

    由于KahaDB是默认的持久化存储方案。所以即使你不配置任何的KahaDB参数信息,Active MQ也会启动KahaDB。
    这种情况下,KahaDB文件所在位置是你的Active MQ安装路径下的/data/KahaDB子目录。

关系型数据库存储方案

    从ActiveMQ 4+版本开始,ActiveMQ就支持使用关系型数据库进行持久化存储——通过JDBC实现的数据库连接。可以使用的关系型数据库囊括了目前市面的主流数据库。

修改Active MQ的配置文件,conf/activemq.xml

1、将将配置文件中的这段配置:

<persistenceAdapter>
	<kahaDB directory="${activemq.base}/data/kahadb"/>
</persistenceAdapter>

修改为为:

<persistenceAdapter>
       <jdbcPersistenceAdapter  dataSource="#mysql-ds "/>
</persistenceAdapter>

2、然后在标签后,增加数据源的配置(与在Spring中的配置一样):
    打开文件conf/activemq.xml文件后我们可以看到熟悉的beans、bean标签等,我们知道Active MQ是用Java语言开发的,那我们看到这些标签是不是可以猜一下Active MQ用没用Spring进行开发呢,答案是肯定的。我们可以在Active MQ的lib/optional文件夹中找到Spring的几个核心依赖包,该xml配置文件也导入了Spring的命名空间。

<bean id="mysql-ds" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/activemq?relaxAutoCommit=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=UTC"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
    <property name="poolPreparedStatements" value="true"/>
</bean>

    其中?relaxAutoCommit=true必须有,其他的属性根据数据库的配置自行决定。
3、将mysql-connector-java-5.1.34-bin.jar(版本可以自行选择)放到ActiveMQ的/ lib目录下。
4、在Mysql数据库中增加在连接字符串中设置的数据库名activemq
5、运行后,会发现在库中增加了3个表
    activemq_acks:用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存,主要数据库字段如下:

  • container:消息的destination
  • sub_dest:如果是使用static集群,这个字段会有集群其他系统的信息
  • client_id:每个订阅者都必须有一个唯一的客户端id用以区分
  • sub_name:订阅者名称
  • selector:选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性and和or操作
  • last_acked_id:记录消费过的消息的id

    activemq_lock:在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,其他的只能作为备份等待Master Broker不可用,才可能成为下一个Master Broker。这个表用于记录哪个Broker是当前的Master Broker。

    activemq_msgs:用于存储消息,Queue和Topic都存储在这个表中。主要的数据库字段如下:

  • id:自增的数据库主键
  • container:消息的destination
  • msgid_prod:消息发送者客户端的主键
  • msg_seq:是发送消息的顺序,msgid_prod+msg_seq可以组成jms的messageid
  • expiration:消息的过期时间,存储的是从1970-01-01到现在的毫秒数
  • msg:消息本体的java序列化对象的二进制数据
  • priority:优先级,从0-9,数值越大优先级越高

消息的持久化订阅

P2P模式缺省把消息进行持久化,而topic模式是没有的。

Topic模式下的消息持久化订阅

正常情况下的topic模式实验:
  1. 启动两个消费者,启动一个生产者,发送消息,两个消费者都可以收到。
  2. 关闭一个消费者,生产者发送消息,活跃的消费者可以收到消息,启动被关闭的消费者,无法收到消息。
  3. 关闭所有消费者,生产者发送消息,在ActiveMQ控制台可以看见消息已被接收,但没有被消费,关闭再启动Active MQ,启动消费者收不到消息。

如果topic模式下,需要消费者在离线又上线后,不管ActiveMQ是否重启过,都保证可以接受到消息,就需要进行持久化订阅

Topic模式消费端代码
public class JmsDurableTopicConsumer {

    private static final String USERNAME
            = ActiveMQConnection.DEFAULT_USER;//默认连接用户名
    private static final String PASSWORD
            = ActiveMQConnection.DEFAULT_PASSWORD;//默认连接密码
    private static final String BROKEURL
            = ActiveMQConnection.DEFAULT_BROKER_URL;//默认连接地址

    public static void main(String[] args) {
        ConnectionFactory connectionFactory;//连接工厂
        Connection connection = null;//连接
        Session session;//会话 接受或者发送消息的线程
        TopicSubscriber messageConsumer;//消费者类型变为TopicSubscriber
        //实例化连接工厂
        connectionFactory = new ActiveMQConnectionFactory(JmsDurableTopicConsumer.USERNAME,
                JmsDurableTopicConsumer.PASSWORD, JmsDurableTopicConsumer.BROKEURL);

        try {
            //通过连接工厂获取连接
            connection = connectionFactory.createConnection();
            //需要有个Id
            connection.setClientID("Mark");//设置客户端ID,需确保ID的唯一性
            //启动连接
            connection.start();
            //创建session
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            //创建一个消息队列
            //消息的destination变为 Topic 
            Topic destination = session.createTopic("DurableTopic2");
            //创建消息消费者-持久订阅
           // 消费者创建时变为session.createDurableSubscriber(destination,
           //"任意名字,代表订阅名 ");
            messageConsumer
                    = session.createDurableSubscriber(destination, "xiangxue");
            messageConsumer.setMessageListener(new MessageListener() {
                public void onMessage(Message message) {
                    try {
                        System.out.println("Accept msg : "
                                + ((TextMessage) message).getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

    Topic模式的发送端是不需要改变的,只需要之前的正常配置就好,因为发送端是默认将消息持久化的,若想将持久化设置取消掉,可以在发送端加上
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
该行代码表示,该发送端发送的消息不会被持久化。消息只会保存在内存中。

接下来,我们测试一下,我们启动了俩个持久订阅的消费者。
在这里插入图片描述

在Active MQ客户端界面上可以看到:
在这里插入图片描述

1、接下来我们发送一条消息;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们可以看到,俩个消费者都已经收到了。

2、我们关掉一个消费者再发一条消息,显然开着的消费者可以收到了topic消息。
在这里插入图片描述

然后,我们打开刚才关闭的消费者,可以看到它收到了我们刚才所发的消息。
在这里插入图片描述

注意观察时间戳!

3、然后我们接着实验,我们将俩个消费者全部关闭,发送一条消息,然后接着关闭我们的Active MQ客户端。
打开数据库activemq-msgs表,可以看到表中多了三条数据,这三条数据正式我们刚才关掉了我们所有的消费者后发送的消息。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4、我们重新启动Active MQ客户端,并打开刚才所关闭的俩个消费者,可以看到俩个消费者仍然可以接收到之前所发送的数据(注意看时间戳)。
在这里插入图片描述

总结上述结果
  1. 运行生产者,发布消息,多个消费者可以正常收到。
  2. 关闭一个消费者,运行生产者,发布消息后再启动被关闭的消费者,可以收到离线后的消息;
  3. 关闭所有消费者,运行生产者,发布消息后,关闭ActiveMQ再启动,启动所有消费者,都可以收到消息。

注意:生产者端无需另外单独配置

消息的非持久化

发送端修改
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
    来设置消息本身的持久化属性为非持久化。
    重复上述实验,可以发现,第1,2点保持不变,但是第三点,当关闭ActiveMQ再启动,消费者关闭后再启动,是收不到消息的。

说明,即使进行了持久订阅,但是消息本身如果是不持久化的,ActiveMQ关闭再启动,这些非持久化的消息会丢失,进行持久订阅的消费者也是收不到自身离线期间的消息的。

发布了32 篇原创文章 · 获赞 13 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_43519048/article/details/104563400