目录
5:JDBC消息存储(http://activemq.apache.org/persistence)
3:修改activemq.xml的jdbcPersistenceAdapter的标签引用数据库配置
4:创建activemq数据库,后重新启动activemq会自动生成对应表
1:activemq_msgs用于存储消息,Queue和Topic都存储在这个表中:
2:activemq_acks用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存:
3:表activemq_lock在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,
2:重启后,代码不变就已经启动了高速缓存;生产者产生消息后如果短时间内没有被消费掉,那么这些消息才会进入数据库落地;
1:ActiveMQ的持久化简介
为避免意外宕机后丢失信息,需要做到重启后可以恢复消息队列;系统消息一般都会采用可持久化机制;
ActiveMQ的消息持久化机制有JDBC,AMQ,KahaDB,LevelDB,无论使用那种持久化机制,消息的存储逻辑是一样的;
就是在发送者将发送消息后,消息中心首先将数据存储在本地文件夹;内存数据库或者远程数据库;然后再发送给接受者,若成功就从存储中删除,失败则继续推送;
消息中心启动后首先检查指定的存储位置,如果有未发送的消息,则需要把消息发送出去;
2:AMQ Meassage Store (了解):
是一种文件存储机制,以前默认的存储机制,AMQ适用于ActiveMQ5.3以前的机制;
3:KahaDB(默认):
1:基于日志文件,从5.4开始默认的持久化插件;
2:默认的配置在/conf/activemq.xml
3:KahaDB的存储原理
1:消息存储是一个事务日志和仅仅使用一个索引文件来存储他所有的位置
2:数据被追加到data logs里面,当不需要log文件中数据的时候,log文件就会被丢弃;
4:KahaDBdata中的文件介绍
1:db-1.log文件
2:db.data
3:db.free:当前db.data那些页是空闲的,文件具体内容是所有空闲也的id
4:db.redo用来进行消息恢复,如果Kaha消息存储在强制退出后启动,用于恢复BTree索引;
5:lock文件锁,表示当前获得KahaDB读写权限的broker
4:LevelDB存储(了解)
5:JDBC消息存储(http://activemq.apache.org/persistence)
1:将mysql数据库的jar包导入到mq服务器
如果想使用其他数据库,就将其他数据库连接jar包和pooljiar包放进去
2:修改activemq.xml,增加数据库配置bean
<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/activemq?relaxAutoCommit=true"/>
<property name="username" value="activemq"/>
<property name="password" value="activemq"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
3:修改activemq.xml的jdbcPersistenceAdapter的标签引用数据库配置
4:创建activemq数据库,后重新启动activemq会自动生成对应表
1: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,数值越大优先级越高
队列消息存贮:
主题存储:
2:activemq_acks用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存:
主要的数据库字段如下:
CONTAINER:消息的Destination
SUB_DEST:如果是使用Static集群,这个字段会有集群其他系统的信息
CLIENT_ID:每个订阅者都必须有一个唯一的客户端ID用以区分
SUB_NAME:订阅者名称
SELECTOR:选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性AND和OR操作
LAST_ACKED_ID:记录消费过的消息的ID。
3:表activemq_lock在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,
其他的只能作为备份等待Master Broker不可用,才可能成为下一个Master Broker。
这个表用于记录哪个Broker是当前的Master Broker。
5:代码验证测试---(一定要开启持久化)
1:队列
生产者:
package com.wkl.queuq;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.jms.*;
/**
* Description:第一次连接ActvieMq的服务
* Date: 2020/9/3 - 下午 1:51
* author: wangkanglu
* version: V1.0
*/
public class Produce {
/*账号密码如果都是默认的admin,可以不用穿;直传url*/
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
/*这个url以tcp协议开头,java程序访问的是61616端口*/
private static final String ACTIVE_URL = "tcp://192.168.43.122:61616";
private static final String Queue_NAME = "queue01";
public static void main(String[] args) throws JMSException {
//1:创建连接工厂,按照给定的账号,密码,url地址来链接;
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(USERNAME,PASSWORD,ACTIVE_URL);
//2:通过链接工厂获得connection并启动
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
//3:通过connection创建会话,参数分别是事务和签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//4:创建目的地(队列还是主题)
Queue queue = session.createQueue(Queue_NAME);
//5:创建消息生产者
MessageProducer producer = session.createProducer(queue);
/*---------开启持久化-----------*/
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
//6:通过消息生产者producer产生3条消息发到mq的队列中
for (int i = 1; i <=5 ; i++) {
//7:创建消息
TextMessage textMessage = session.createTextMessage("msg--" + i);
//8:通过perducer发送给mq
producer.send(textMessage);
}
//9:关闭资源;顺着申请,倒着关闭
producer.close();
session.close();
connection.close();
System.out.println("---end---");
}
}
消费者:
package com.wkl.queuq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* Description:消息的消费者就是处理者的helloword
* Date: 2020/9/3 - 下午 2:38
* author: wangkanglu
* version: V1.0
*/
public class Consumer {
/*账号密码如果都是默认的admin,可以不用穿;直传url*/
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
/*这个url以tcp协议开头,java程序访问的是61616端口*/
private static final String ACTIVE_URL = "tcp://192.168.43.122:61616";
private static final String Queue_NAME = "queue01";
public static void main(String[] args) throws JMSException {
System.out.println("1-----");
//1:创建连接工厂,按照给定的账号,密码,url地址来链接;
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(USERNAME,PASSWORD,ACTIVE_URL);
//2:通过链接工厂获得connection并启动
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
//3:通过connection创建会话,参数分别是事务和签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//4:创建目的地(队列还是主题)
Queue queue = session.createQueue(Queue_NAME);
//5:创建消息消费者
MessageConsumer consumer = session.createConsumer(queue);
//消息的消费者循环读取读取消息
while(true){
//6:consumer读取消息,当初发送消息是testMessage,接受时也是这个类型
//消费者只等待4秒;4秒后没消息就走了
TextMessage textMessage = (TextMessage) consumer.receive(4000L);
if(null!=textMessage){
System.out.println("接收到的消息:"+textMessage.getText());
// textMessage.acknowledge();
}else {
break;
}
}
//7:关闭资源;顺着申请,倒着关闭
consumer.close();
session.close();
connection.close();
System.out.println("----end----");
}
}
2:主题
订阅者:
package com.wkl.PersistenceTopic;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
/**
* Description:持久化的消费者改造
* Date: 2020/9/3 - 下午 8:29
* author: wangkanglu
* version: V1.0
*/
public class Consumer {
/*账号密码如果都是默认的admin,可以不用穿;直传url*/
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
/*这个url以tcp协议开头,java程序访问的是61616端口*/
private static final String ACTIVE_URL = "tcp://192.168.43.122:61616";
private static final String TOPIC_NAME = "topic01";
public static void main(String[] args) throws JMSException, IOException {
System.out.println("zhangsan");
//1:创建连接工厂,按照给定的账号,密码,url地址来链接;
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(USERNAME,PASSWORD,ACTIVE_URL);
//2:通过链接工厂获得connection并启动
Connection connection = activeMQConnectionFactory.createConnection();
//设定是谁订阅了我
connection.setClientID("zhansan");
//3:通过connection创建会话,参数分别是事务和签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//4:创建目的地(队列还是主题)
Topic topic = session.createTopic(TOPIC_NAME);
//创建主题订阅者
TopicSubscriber topicSubscriber = session.createDurableSubscriber(topic,"备注。。。");
//5:启动
connection.start();
Message message = topicSubscriber.receive();
while(null!=message){
TextMessage textMessage = (TextMessage) message;
System.out.println("收到消息:" + textMessage.getText());
message = topicSubscriber.receive(5000L);
}
session.close();
connection.close();
System.out.println("----end----");
}
}
发布者:
package com.wkl.PersistenceTopic;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* Description:持久化的topic生产
* Date: 2020/9/3 - 下午 8:20
* author: wangkanglu
* version: V1.0
*/
public class Produce {
/*账号密码如果都是默认的admin,可以不用穿;直传url*/
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
/*这个url以tcp协议开头,java程序访问的是61616端口*/
private static final String ACTIVE_URL = "tcp://192.168.43.122:61616";
private static final String TOPIC_NAME = "topic01";
public static void main(String[] args) throws JMSException {
//1:创建连接工厂,按照给定的账号,密码,url地址来链接;
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, ACTIVE_URL);
//2:通过链接工厂获得connection并启动
Connection connection = activeMQConnectionFactory.createConnection();
//3:通过connection创建会话,参数分别是事务和签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//4:创建目的地(队列还是主题)
Topic topic = session.createTopic(TOPIC_NAME);
//5:创建消息生产者
MessageProducer producer = session.createProducer(topic);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
//先表明持久化才启动
connection.start();
//6:通过消息生产者producer产生3条消息发到mq的主题中
for (int i = 1; i <= 3; i++) {
//7:创建消息
TextMessage textMessage = session.createTextMessage("TOPICmsg--" + i);
//8:通过perducer发送给mq
producer.send(textMessage);
}
//9:关闭资源;顺着申请,倒着关闭
producer.close();
session.close();
connection.close();
System.out.println("---end---");
}
}
订阅者消费完了;对应的消息这个还存在;
6:总结
1:用哪个数据库,就将相应的jar包放进mq的lib文件夹中
2:设置数据库连接bean的时候,mysql5,和mysql8是不同的;不同的驱动和不同的url
3:createTablesOnStartup属性第一次启动创建表后;记得把他这只为false
6:增强的JDBC,高速缓存Journal
1:jounal的持久化适配器---配置
<persistenceFactory>
<journalPersistenceAdapterFactory
journalLogFiles="5"
journalLogFileSize="32768"
useJournal="true"
useQuickJournal="true"
dataSource="#mysql-ds"
dataDirectory="activemq-data" />
</persistenceFactory>
2:重启后,代码不变就已经启动了高速缓存;生产者产生消息后如果短时间内没有被消费掉,那么这些消息才会进入数据库落地;
7:持久化总结