1. 消费者代码编写
package com.sxz.activemqconsumer;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
/**
* @author song.xinzhi
* @Destination: TODO
* @date: 2020/4/12
*/
public class TopicConsumerPersist {
public static final String MQ_URL = "tcp://192.168.46.128:61616";
/**
*
* @param args
* @throws JMSException
* @throws IOException
*/
public static void main(String[] args) throws JMSException, IOException {
//1. 创建一个工程连接类,以及session会话
String userId = "songxinzhi01";
System.out.println(userId + ":订阅主题begin....");
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(MQ_URL);
Connection connection = connectionFactory.createConnection();
connection.setClientID(userId);
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//2. 创建topic主题
Topic topic = session.createTopic("persist-topic");
//3. 创建持久化的订阅者
TopicSubscriber topicSubscriber = session.createDurableSubscriber(topic, userId);
connection.start();
//4. 订阅者接收消息
Message message = topicSubscriber.receive();
while (null != message) {
TextMessage textMessage = (TextMessage)message;
System.out.println(userId + ":收到持久化topic:" + textMessage.getText());
message = topicSubscriber.receive(5000L);
}
session.close();
connection.close();
}
}
启动消费者,观察MQ控制台,topic和Subscribers变化
Subscribers是刚才启动的订阅者在线
把消费者进程停掉观察Subscribers发现已经下线
2. 编写生产者代码
package com.sxz.activemqprovider;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* @author song.xinzhi
* @Destination: TODO
* @date: 2020/4/12
*/
public class TopicProducerPersist {
public static final String MQ_URL = "tcp://192.168.46.128:61616";
public static void main(String[] args) throws JMSException, InterruptedException {
//1、创建工厂连接对象以及连接对象
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(MQ_URL);
Connection connection = connectionFactory.createConnection();
//2、使用连接对象创建会话(session)对象
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//3、使用会话对象创建目标topic对象
Topic topic = session.createTopic("persist-topic");
//4、使用会话对象创建生产者对象
MessageProducer producer = session.createProducer(topic);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
//5、开启连接
connection.start();
//6、使用会话对象创建一个消息对象,发送消息
for (int i = 1; i <=8; i++) {
Thread.sleep(5000L);
TextMessage textMessage = session.createTextMessage("persist-topic_" + i);
producer.send(textMessage);
}
//9、关闭资源
producer.close();
//10、关闭会话
session.close();
//11、关闭连接
connection.close();
}
}
上面消费者先在MQ上注册过了,后面又停掉了,我们发现订阅者从Active Durable Topic Subscribers列表移到了Offline Durable Topic Subscribers也就是从上线到下线的体现,此时消费者已经停掉,我们再启动生产者,发现此时生产者发出了5条消息等待消费者消费
这个时候我们再启动消费者
idea控制台打印出了消费了这5条消息,同时MQ的控制台也展示5条消息被消费了,这个测试说明,只要是消费者现在MQ上订阅过,就算消费者的客户端宕机了,重新启动后,还是会接收到生产者发布的消息 如果生产者先产生5条消息,消费者从来没有订阅过,如下先启动生产者,生产了5调消息在MQ服务器上,等待消费
启动消费者
然而根据程序控制台和MQ控制台现象发现,5条消息并没有被生产者消费掉,也就是说明如果想消费生产者的消息之前必须要先订阅注册
3. ActiveMQ 持久化方案
持久化机制有JDBC,AMQ,KahaDB,LevelDB,可以参考官网:http://activemq.apache.org/persistence
5.3版本以后默认的是KahaDB
JDBC 存储
- 在安装的Mysql数据库中lib中添加依赖jar包
- 修改activemq.xml配置文件
在persistenceAdapter加入如下配置<!--createTablesOnStartup 启动是否创建表 第一次为true 后续为false,第一次为true是为了创建表,之后的每次都不创建,使用第一次创建的表保存数据-->
<jdbcPersistenceAdapter dataSource="#activemq-db" createTablesOnStartup="true" />
3. 配置数据源,在beans节点中
<bean id="activemq-db"class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.46.128:3306/activemq?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="maxActive" value="200"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
- 启动ActiveMQ,进程可以正常启动,可以看到数据库多了三张表
activemq_acks: 用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存,主要的数据库字段如下:
CONTAINER:消息的Destination
SUB_DEST:如果是使用Static集群,这个字段会有集群其他系统的信息CLIENT_ID:每个订阅者都必须有一个唯一的客户端ID用以区分SUB_NAME:订阅者名称
SELECTOR:选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性AND和OR操作
LAST_ACKED_ID:记录消费过的消息的ID。
activemq_msgs: 用于存储消息,Queue和Topic都存储在这个表中:ID:自增的数据库主键
CONTAINER:消息的Destination
MSGID_PROD:消息发送者客户端的主键
MSG_SEQ:是发送消息的顺序,
MSGID_PROD+MSG_SEQ可以组成JMS的Message
IDEXPIRATION:消息的过期时间,存储的是从1970-01-01到现在的毫秒数MSG:消息本体的Java序列化对象的二进制数据
PRIORITY:优先级,从0-9,数值越大优先级越高
**activemq_lock:**在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,其他的只能作为备份等待Master Broker不可用,才可能成为下一个Master Broker。这个表用于记录哪个Broker是当前的Master Broker。