- 官方链接:http://activemq.apache.org/persistence
- 之前已经简单地聊过了在代码层面,如何选择是否消息持久化链接,而在这一篇,将着眼于ActiveMQ的持久化的底层实现的选择。
1. 概述
- 持久化,就是将消息存储在本地数据文件或者数据库当中,实现关机、掉电数据也不会损失的技术。
- 而针对ActiveMQ 来说,有几种常见的技术:
- AMQ Message Store
- kahaDB
- JDBC消息存储
- LevelDB消息存储
- JDBC Message Store with ActiveMQ Journal
2. AMQ Message Store
- 基于文件的存储机制,是以前的默认机制,现在不再使用。
- AMQ是一种文件存储形式,它具有写入速度快和容易恢复的特点。消息存储再一个个文件中文件的默认大小为32M,当一个文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。AMQ适用于ActiveMQ5.3之前的版本
3. kahaDB
- 官方文档:http://activemq.aache.org/kahadb
- 该存储机制,自ActiveMQ 5.4开始是默认的持久化插件。
- 在配置文件中,可以看出,目前使用的的确是这个。
- 而kahaDB的文件,存储在data文件夹中
- 上面几个文件实现了保存所有持久化消息。
db-1.log
:真实保存所有数据的地方,当一个文件满了之后,就会开第二个,db-2.log
以此类推。当不再有引用到数据文件中的任何消息时,文件会被删除或归档。db.data
:索引文件,底层就是BTree,提高查找的能力。db.free
:当前db.data文件里哪些页面是空闲的,文件具体内容是所有空闲页的IDdb.redo
:用来做消息恢复,恢复BTree索引。lock
:表示当前获得kahadb读写权限的broker
4. JDBC消息存储
- 其实就是将数据持久化到数据库中。
4.1 修改成JDBC消息存储
- 首先导包,需要在
lib
文件夹中,加入mysql的java驱动文件,比如说mysql-connector-java-5.1.47.jar
- 然后,修改配置文件,之前默认使用的是kahaDB,现在把它注释掉,然后修改成jdbc的。注意,千万别加
createTableOnStartup="true"
这个属性,不然跑的时候就会报错,很坑爹!!<persistenceAdapter> <jdbcPersistenceAdapter dataSource="#mysql-ds"/> </persistenceAdapter>
- 那个配置文件实际上就是Spring的配置文件,现在在里面选择使用JDBC的持久化方式,给了它一个数据库连接池的参数
mysql-ds
,因此我们还需要在后面将数据库连接池注入到IOC容器中。... </broker> <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://192.168.233.1/activemq?relaxAutoCommit=true"/> <property name="username" value="root"/> <property name="password" value="root"/> <property name="poolPreparedStatements" value="true"/> </bean> <import resource="jetty.xml"/>
在这个文件里,建议将数据库声明在broker下面,import上面
- 数据库新建一个库,叫做
activemq
,因为上面的连接已经确定了,登录activemq
的数据库。create database activemq;
- 重启服务器,如果没有意外的话,数据库上面会多出几个表
- activemq_msgs:用于存储所有持久化的消息(包括queue和topic)
- activemq_acks:用于存储订阅的主题的相关信息。也就是在topic持久化的时候,订阅者一开始在activemq注册确认的那些信息。
- activemq_lock:在集群的时候有用,用于记录哪个Broker是当前的Master Broker。
- activemq_msgs:用于存储所有持久化的消息(包括queue和topic)
4.2 测试
4.3 持久化的Queue消息
- 只有
activemq_msgs
会发生改变,会将所有消息存储到该表中。 - 当有消费者消费该消息之后,数据库的消息会直接删除。
4.4 持久化的Topic消息
- 如果是第一次持久化订阅,则会首先在
activemq_acks
中保存订阅的相关信息 - 之后,如果有该主题相关的消息,就会将消息持久化到
activemq_msgs
中。 - 值得一提的是,消息会持久的存储在数据库中,即使有订阅者接受了,还是会存储,并不会删掉。
- 持久化的topic大量数据后可能导致性能下降。
4.3 分析
- 频繁地读写数据库,反而会拉低MQ服务器自身的性能。违背了MQ本来就是来异步、削峰、解耦的。
- 而且有一些Queue消息是很快就可以被消费掉然后删除的,其实是没必要存储在数据库的。
- 因此,就有一种新的策略出现。
5. JDBC Message Store with ActiveMQ Journal
- 就像缓存和数据库之间的关系,通过缓存可以挡掉很大部分的数据库读写。
- ActiveMQ也有策略可以挡掉大部分的数据库读写。那就是在读写数据库之前,加一层缓存文件ActiveMQ Journal。
- 这是一个高速缓存写入技术,当生产者生产消息之后,先存储在journal文件中,如果在这段时间内有消费者消费了该消息,则将该消息从journal文件中删除。那些在journal文件存储较长时间的消息才会存储到数据库中。
5.1 配置
- 又需要修改配置文件了。将上面设置的持久化方式注释掉,改成这样的。
<persistenceFactory> <journalPersistenceAdapterFactory journalLogFiles="5" journalLogFileSize="32768" useJournal="true" useQuickJournal="true" dataSource="#mysql-ds" dataDirectory="${activemq.data}/a-j-data" /> </persistenceFactory>
- 修改完直接运行就好了。可以尝试测试,发消息之后,查看数据库一开始并没有消息,只有过了一会之后,才会有消息真正持久化到数据库中。
6. levelDB
- 这种文件系统是从ActiveMQ5.8之后引进的,它和KahaDB非常相似,也是基于文件的本地数据库存储形式,但是它提供比KahaDB更快的持久性。
- 但它不使用自定义B-Tree实现来索引独写日志,而是使用基于LevelDB的索引
- 但是,由于该技术的成熟度还不够,因此一般还是使用kahaDB
6.1 配置方式
<persistenceAdapter>
<levelDB directory="activemq-data"/>
</persistenceAdapter>
7. 总结
- 演变过程:从最初的AMQ Message Store方案到ActiveMQ V4版本退出的High Performance Journal(高性能事务支持)附件,并且同步推出了关于关系型数据库的存储方案。ActiveMQ5.3版本又推出了对KahaDB的支持(5.4版本后被作为默认的持久化方案),后来ActiveMQ 5.8版本开始支持LevelDB,到现在5.9提供了标准的Zookeeper+LevelDB集群化方案。
- 无论使用哪一种持久化方式,消息存储的逻辑都是一致的。
- 下一章,将会详细描述通过LevelDB+Zookeeper实现Master-Slaver的数据备份方式