【笔记于学习尚硅谷课程所作】
9、ActiveMQ的消息存储和持久化
9.1 MQ持久化介绍
为了避免意外宕机以后丢失信息,需要做到重启后可以恢复消息队列,消息系统一般都会采用持久化机制。ActiveMQ的消息持久化机制有JDBC,AMQ,KahaDB 和LevelDB,无论使用哪种持久化方式,消息的存储逻辑都是一致的。
就是在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等再试图将消息发送给接收者,成功则将消息从存储中删除,失败则继续尝试发送。
消息中心启动以后首先要检查指定的存储位置,如果有未发送成功的消息,则需要把消息发送出去。
9.2 MQ持久化机制的种类
- AMQ Message Store(了解):基于文件的存储方式,以前的默认消息存储方式
- KahaDB消息存储(默认):基于日志文件的存储方式,现在的默认的存储方式
- JDBC消息存储:基于JDBC的存储方式
- LevelDB消息存储(了解):基于文件的存储方式,使用LevelDB的索引方式
- JDBC Message store with ActiveMQ Journal:JDBC存储的加强,加入高速缓存机制,减少数据库读写负担
- Replicated LevelDB Store:基于LevelDB和Zookeeper的数据复制方式,用于Master-slave方式的首选数据复制方案。
9.3 KahaDB的存储原理
消息存储使用一个事务日志和仅仅用一个索引文件来存储它所有的地址。
kahadb在消息保存目录中有4类文件和一个lock
- db- .log:KahaDB存储消息到预定义大小的数据记录文件中,文件命名为db-.log。当数据文件己满时,一个新的文件会随之创建,number数值也会随之递增,它随着消息数量的增多。当不再有引用到数据文件中的任何消息时,文件会被删除或归档。
- db.data:该文件包含了持久化的BTree索引,索引了消息数据记录中的消息,它是消息的索引文件,本质上是B-Tree (B树),使用B-Tree作为索引指向db- .log里面存储的消息。
- db.free:当前db.data文件里哪些页面是空闲的,文件具体内容是所有空闲页的ID
- db.redo:用来进行消息恢复,如果KahaDB消息存储在强制退出后启动,用于恢复BTree索引。
- lock:文件锁,表示当前获得kahadb读写权限的broker。
9.4 JDBC配置mysql
1.添加mysql数据库的驱动包到lib文件夹(mysql-connector-java-5.1.38. jar)
2.修改activemq.xml配置文件
<!--将默认的kahaDB换成jdbcPersistenceAdapter-->
<persistenceAdapter>
<jdbcPersistenceAdapter dataSource="#mysql-ds" createTablesOnStartup="true" />
</persistenceAdapter>
<!--
dataSource指定将要引用的持久化数据库的bean名称,
createTablesOnStartup是否在启动的时候创建数据表,默认值是true,
-->
3.在activemq.xml中添加数据库连接池配置
<!--插入位置</broker>与<import resource="jetty.xml">之间-->
<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"/>
<property name="username" value="root"/>
<property name="password" value="123456" />
<property name="maxTotal" value="200"/>
<property name="poolPreparedStatements" value="true" />
</bean>
4.创建数据库和表的说明
创建数据库
CREATE DATABASE activemq;
重启activemq服务后会默认生成三张表
- ACTIVEMQ_ MSGS:消息表
- ACTIVEMQ_ ACKS:存储订阅关系
- ACTIVEMQ_ LOCK:在集群环境中才有作用,这个表用于记录哪个Broker是Master Broker
(1)ACTIVEMQ_ MSGS表的说明
列 | 含义 |
---|---|
ID | 自增的数据库主键 |
CONTAINER | 消息的Destination |
MSGID_ PROD | 消息发送者的主键 |
MSG_ SEQ | 是发送消息的顺序,MSGID_ PROD+MSG_SEQ 可以组成JMS的MessagelD |
EXPIRATION | 消息的过期时间,存储的是从1970-01-01到现在的毫秒数 |
MSG | 消息本体的Java序列化对象的二进制数据 |
PRIORITY | 优先级,从0-9,数值越大优先级越高 |
(2)ACTIVEMQ_ ACKS表的说明
列 | 含义 |
---|---|
CONTAINER | 消息的Destination |
SUB_DEST | 如果是使用Static集群,这个字段会有集群其他系统的信息 |
CLIENT_ ID | 每个订阅者都必须有一个唯一的客户端ID用以区分 |
SUB_ NAME | 订阅者名称 |
SELECTOR | 选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性AND和OR操作 |
LAST_ ACKED_ ID | 记录消费过的消息的ID |
(3)ACTIVEMQ_ LOCK表的说明
列 | 含义 |
---|---|
ID | 自增的数据库主键 |
BrokerName | 拥有锁的Broker名称 |
5.开启消息持久化
//只有开启了消息持久化,消息才会进入数据库
//在生产者代码中添加
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
6.注意
- 记得需要使用到的相关jar文件放置到ActiveMQ安装路径下的lib目录。mysql-jdbc驱动的jar包和对应的数据库连接池jar包
- 在jdbcPersistenceAdapter标签中设置了createTablesOnStartup属性为true时在第一次启动ActiveMQ时, ActiveMQ服务 节点会自动创建所需要的数据表。启动完成后可以去掉这个属性,或者更改create TablesOnStartup属性为false.
- “java.lang.llegalStateException: BeanFactory not itilized or already closed”这是因为您的操作系统的机器名中有“_”符号。请更改机器名并且重启后即可解决问题。
9.5 JDBC的加强
ActiveMQ Journal,使用高速缓存写入技术,大大提高了性能。
当消费者的消费速度能够及时跟上生产者消息的生产速度时,journal文件能够大大减少需要写入到DB中的消息。
eg:生产者生产了1000条消息,这1000条消息会保存到journal|文件,如果消费者的消费速度很快的情况下,在journal文 件还没有同步到DB之前,消费者已经消费了90%的以上的消息,那么这个时候只需要同步剩余的10%的消息到DBp如果消费者的消费速度很慢,这个时候journal文件可以使消息以批量方式写到DB。
<!--修改activemq.xml,使用journalPersistenceAdapterFactory覆盖jdbcPersistenceAdapter-->
<persistenceFactory>
< journalPersistenceAdapterFactory
journalLogFiles="4"
journalLogFileSize="32768"
useJournal="true"
useQuickJournal="true"
dataSource="#mysql-ds"
dataDirectory="activemq-data"/>
</persistenceFactory>