1.消息持久化机制
- 消息持久化机制:为避免消息系统意外宕机而导致丢失信息,消息中间件一般会支持消息服务器实例重启服务器实例以恢复原来的消息数据。
- ActiveMQ的常见的消息持久化机制包括JDBC、AMQ、KahaDB、LevelDB,无论使用哪种持久化方式,消息的存储逻辑都是一致的。
在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或远程数据库等再试图将消息发送给接收者,成功则将消息从存储中删除,失败则继续尝试发送。消息中心启动后首先要检查指定的存储位置,若有未发送成功的消息,则需要把消息发送出去。
1.1 AMQ
基于文件存储,它具有写入速度快和容易恢复的特定,消息存储在一个个文件中,文件的默认大小为32M,当一个存储文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段这个文件被删除,AMQ适用于ActiveMQ5.3之前的版本。
缺点:会为每个destination创建一个索引文件
1.2 KahaDB(5.4以来默认)
基于本地日志文件、索引和缓存。所有的destination用一个索引文件来存储他的地址,在/data/kahadb/ 下
-
db-< number>.log(主要存数据)是KahaDB存储消息到预定义大小的数据记录文件,文件命名为db-< number>.log,当数据文件已满时,一个新的文件会随之创建,number数值也会随之递增,它随之消息数量的增多,如每32M一个文件,文件名按照数字进行编号,如sb-1.log……当不再有引用到数据文件中的任何消息时,文件会被删除或归档。
-
db.data文件包含了持久化的BTree索引,索引了消息数据记录中的消息,它是消息的索引文件,本质上是B-Tree(B树),使用B-Tree作为索引指向db-.log里面存储的消息。
-
db.free文件表示当前db.data文件哪些页面是空闲的,文件具体内容是所有空闲页的ID。
-
db.redo文件是用来进行消息恢复,如果KahaDB消息存储在强制退出后启动,用于恢复BTree索引。
-
lock文件表示当前获得KahaDB读写权限的broker。
1.3 JDBC
基于数据库存储,其存储性能是最差的。。。
1.4 LevelDB(5.8+)
从ActiveMQ5.8之后引进的,它和KahaDB很相似,也是基于文件的本地数据库存储形式,但是它提供比KahaDB更快的持久性,但它不再使用自定义B-Tree实现来索引预写日志,而是使用基于LevelDB的索引。其索引具有几个不错的属性:
- 快速更新(无需进行随机磁盘更新)
- 并发读取
- 使用硬链接快速索引快照
KahaDB和LevelDB存储都必须定期执行垃圾收集周期,以确定可以删除哪些日志文件。KahaDB由于增加了存储的数据量并且在收集发生时可能导致读/写停顿,因此可能非常慢。LevelDB存储使用更快的算法来确定何时收集日志文件并避免这些停顿。
2. 消息持久化配置
2.1 kahaDB
activemq.xml
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
2.2 JDBC
activemq.xml
<persistenceAdapter>
<!--<kahaDB directory="${activemq.data}/kahadb"/>-->
<jdbcPersistenceAdapter dataSource="#mysql-ds" createTablesOnStartup="false"/>
</persistenceAdapter>
- dataSource指定将要引用的持久化数据库的bean名称,createTablesOnStartup表示是否在启动时创建数据表,默认值为true,这样每次启动都会创建数据表,一般是第一次启动的时候设置为true之后改为false
- 添加mysql数据库的驱动jar包到activwmq/lib的文件夹下
注意:若使用第三方连接池或是连接器(例如c3p0、druid),应同时将其jar包添加到lib文件夹下 - 数据库连接池配置(使用官方自带的连接池),将其配置到activemq.xml配置文件中的</ broker>标签之后
<bean id="mysql-ds" class="org.apache.commons.dbcp.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>
- 自动创建的三张数据表说明:
- 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,数值越大优先级越高 - activemq_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。
- 注意:
如果是queue模式,在没有消费者的情况下会将消息保存到activemq_msgs表中,只要有任意一个消费者已经消费过,相应的消息将会立即被删除。
如果是topic模式,一般是先启动消费订阅然后再生产的情况下会将消息保存到activemq_acks表中。
若报错“java.lang.lllegalStateException:BeanFactory not initialized or already closed”这是因为操作系统的机器名中有“_”符号,更改机器名并且重启后即可解决。
2.3 LevelDB
activemq.xml
<persistenceAdapter>
<levelDB directory="activemq-data"/>
</persistenceAdapter>