我们在使用原生ActiveMQ的API编程中,介绍ActiveMQ的使用过程中,在介绍其Point-to-Point(P2P) /点对点模式时,我们发现在该模式下消息时不会丢失的
那么这里是如果做到消息的持久化呢?ActiveMQ提供了几种持久化方式:
- AMQ消息存储,基于文件的存储方式,它具有写入速度快和容易恢复的特点。消息存储在一个个文件中,文件的默认大小为32M,如果一条消息的大小超过了32M,那么这个值必须设置大一点。当一个存储文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。AMQ适用于ActiveMQ5.3之前的版本。
- KahaDB消息存储,提供了容量的提升和恢复能力,是现在的默认存储方式;KahaDB是基于文件的本地数据库储存形式,虽然没有AMQ的速度快,但是它具有强扩展性,恢复的时间比AMQ短,从5.4版本之后KahaDB做为默认的持久化方式。
- JDBC消息存储,消息基于JDBC存储的。(JDBC Message Store with ActiveMQ Journal优化了JDBC Store消息存储)
- Memory消息存储,基于内存的消息存储,就是消息存储在内存中。这里没有动态的缓存存在,所以你必须注意设置你的broker所在的JVM和内存限制。这种方式的持久化消息只在当前JVM内有效,当重启JVM之后会丢失持久化的消息。
这里我们就来看一看ActiveMQ的conf路径下的activemq配置文件,如下:
从上述可以看出,ActiveMQ默认的持久化机制为KahaDB消息存储,所以即使你不配置任何的KahaDB参数信息,ActiveMQ也会启动KahaDB。这种情况下,KahaDB文件所在位置是你的ActiveMQ安装路径下的/data/kahadb
子目录,如下:
- db.data 它是消息的索引文件,本质上是B-Tree(B树),使用B-Tree作为索引指向
db-*.log
里面存储的消息 - db.redo 用来进行消息恢复
- db-*.log 存储消息内容。新的数据以APPEND的方式追加到日志文件末尾。属于顺序写入,因此消息存储是比较 快的。默认是32M,达到阀值会自动递增
- lock 文件锁,写入当前获得kahadb读写权限的broker ,用于在集群环境下的竞争处理
系统中默认的kahadb的配置,只为我们配置的directory
来指定文件的路径,其实我们还可以配置更多的属性,如下:
- director: KahaDB存放的路径,默认值activemq-data
- indexWriteBatchSize: 批量写入磁盘的索引page数量,默认值为1000
- indexCacheSize: 内存中缓存索引page的数量,默认值10000
- enableIndexWriteAsync: 是否异步写出索引,默认false
- journalMaxFileLength: 设置每个消息data log的大小,默认是32MB
- enableJournalDiskSyncs: 设置是否保证每个没有事务的内容,被同步写入磁盘,JMS持久化的时候需要,默认为true
- cleanupInterval: 在检查到不再使用的消息后,在具体删除消息前的时间,默认30000
- checkpointInterval: checkpoint的间隔时间,默认是5000
- ignoreMissingJournalfiles: 是否忽略丢失的消息日志文件,默认false
- checkForCorruptJournalFiles: 在启动的时候,将会验证消息文件是否损坏,默认false
- checksumJournalFiles: 是否为每个消息日志文件提供checksum,默认false
- archiveDataLogs: 是否移动文件到特定的路径,而不是删除它们,默认false
- directoryArchive: 定义消息已经被消费过后,移动data log到的路径,默认null
- databaseLockedWaitDelay: 获得数据库锁的等待时间(used by shared master/slave),默认10000
- maxAsyncJobs: 设置最大的可以存储的异步消息队列,默认值10000,可以和concurrent MessageProducers设置成一样的值。
- concurrentStoreAndDispatchTransactions: 是否分发消息到客户端,同时事务存储消息,默认true
- concurrentStoreAndDispatchTopics: 是否分发Topic消息到客户端,同时进行存储,默认true
- concurrentStoreAndDispatchQueues: 是否分发queue消息到客户端,同时进行存储,默认true
从ActiveMQ 4+版本开始,ActiveMQ就支持使用关系型数据库进行持久化存储——通过JDBC实现的数据库连接。可以使用的关系型数据库囊括了目前市面的主流数据库。
接下来我们就来将默认的KahaDB的存储机制改为JDBC的形式,首先我们在conf下的activemq.xml
中,将默认配置的kahbdb改成jdbc,如下
然后我们就需要配置上述指定的名为dataSource的数据库连接,其实为Spring的配置是类似的,如下:
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/activemq?relaxAutoCommit=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
注:其中url属性值中配置的&
即&
的转义字符,其中第一个属性relaxAutoCommit=true
是必需的。
然后我们在本地数据中建立一个相同的数据库名activemq
即口,然后就可以启动我们的ActiveMQ服务了,在启动ActiveMQ之前,我们需要在ActiveMQ安装目录下的lib目录下添加相关依赖jar包,就是用于上述创建和数据库的连接使用
这里版本没有太大要求,但是最好保持一致,比如这里我们引入dbcp2的包,那么配置文件中肯定也要是dbcp2.BasicDataSource
添加完依赖jar包后,就可以启动ActiveMQ了,然后我们就会发现在activemq数据库中多出了三种表,如下:
- ACTIVEMAQ_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: 消息发送者在客户端的主键
- MSGID_SEQ: 是发送消息的顺序,MSGID_PROD + MSGID_SEQ可以组成JMS的MessageID
- EXPIRATION: 消息的过期时间,存储的是从1970-01-01到现在的毫秒数
- MSG: 消息本体的java序列化对象的二进制数据
- PRIORITY: 优先级,从0-9,数值越大优先级越高
这里我们在Point-to-Point(P2P) /点对点模式下,来启动一个消息生成者,但是不启动消费者,然后来看看ACTIVEMQ_MSGS表的信息,如下:
这里我们就可以看到消息生成者发送的三条数据,消费者还未进行消费,就可以在ACTIVEMQ_MSGS查看到信息,然后我们在启动消息的消费者,该表中的三条数据就会被消费掉了(即表中数据被删除)
JDBC Message Store with ActiveMQ Journal可以看做优化版的JDBC存储,这种方式克服了JDBC Store的不足,JDBC存储每次消息过来,都需要去写库和读库。而JDBC Message Store with ActiveMQ Journal使用延迟存储数据到数据库,当消息来到时先缓存到文件中,延迟后才写到数据库中。
当消费者的消费速度能够及时跟上生产者消息的生产速度时,journal文件能够大大减少需要写入到DB中的消息。 举个例子,生产者生产了1000条消息,这1000条消息会保存到journal文件,如果消费者的消费速度很快的情况 下,在journal文件还没有同步到DB之前,消费者已经消费了90%的以上的消息,那么这个时候只需要同步剩余的 10%的消息到DB。 如果消费者的消费速度很慢,这个时候journal文件可以使消息以批量方式写到DB。
其配置如下:
<persistenceFactory>
<journalPersistenceAdapterFactory journalLogFiles="4" journalLogFileSize="32768" useJournal="true" useQuickJournal="true" dataSource="#dataSource" dataDirectory="activemq-data"/>
</persistenceFactory>
Memory内存消息存储主要是存储所有的持久化的消息在内存中。这里没有动态的缓存存在,所以你必须注意设置你的broker所在的JVM和内存限制。
这种方式的持久化消息只在当前JVM内有效,当重启JVM之后会丢失持久化的消息。
配置方式如下:只需要将persistent
属性设为false即可
还有一种就是AMQ消息存储,它是一个基于文件、事务存储设计为快速消息存储的一个结构,该结构是以流的形式来进行消息交互的。
其配置如下: