ActiveMQ消息中间件的持久化和事务(第二章)

699 阅读4分钟

API 持久化 优先级 事务 持久化方式 ack 安全认证

Active MQ的持久化

我们发送到MQ里的消息,如果还没有来得及消费MQ里的消息,但是MQ服务器重启了,MQ里的消息是不是就消失了,丢失了消息就说明系统不稳定啊。然后伟大的科学家们就研究出来了把消息持久化存储大方法避免丢消息。

KahaDB存储

他是是默认的持久化策略
在data/kahadb这个目录下,会生成四个文件,来完成消息持久化

  1. db.data 它是消息的索引文件,本质上是B-Tree(B树),使用B-Tree作为索引指向db-*.log里面存储的消息

  2. db.redo 用来进行消息恢复 *

  3. db-.log 存储消息内容。新的数据以APPEND的方式追加到日志文件末尾。属于顺序写入,因此消息存储是比较 快的。默认是32M,达到阀值会自动递增

  4. lock文件 锁,写入当前获得kahadb读写权限的broker ,用于在集群环境下的竞争处理 特性:

  5. 日志形式存储消息。

  6. 消息索引以 B-Tree 结构存储,可以快速更新。

  7. 完全支持 JMS 事务。

  8. 支持多种恢复机制kahadb 可以限制每个数据文件的大小。不代表总计数据容量。 配置:

<persistenceAdapter>
<!--directory:保存数据的目录;journalMaxFileLength:保存消息的文件大小 --> 
	<kahaDBdirectory="${activemq.data}/kahadb"journalMaxFileLength="16mb"/> 
</persistenceAdapter>

JDBC存储

使用JDBC持久化方式,数据库默认会创建3个表,每个表的作用如下:

  1. activemq_msgs:queue和topic的消息都存在这个表中
  2. activemq_acks:存储持久订阅的信息和最后一个持久订阅接收的消息ID
  3. activemq_lock:跟kahadb的lock文件类似,确保数据库在某一时刻只有一个broker在访问 ActiveMQ 将数据持久化到数据库中。
    不指定具体的数据库。可以使用任意的数据库存储。
    例如: MySQL 数据库。 下述文件为 \conf\activemq.xml 配置文件部分内容。 首先定义一个 mysql-ds 的 MySQL 数据源,然后在 persistenceAdapter 节点中配置 jdbcPersistenceAdapter 并且引用刚才定义的数据源。

dataSource 指定持久化数据库的 bean,createTablesOnStartup 是否在启动的时候创建数 据表,默认值是 true,这样每次启动都会去创建数据表了,一般是第一次启动的时候设置为 true,之后改成 false。
\conf\activemq.xml

Beans中添加

<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="root"/>
  <property name="password" value="123456"/>
  <property name="maxActive" value="200"/>
  <property name="poolPreparedStatements" value="true"/> 
</bean>

修改persistenceAdapter

<persistenceAdapter>
<!-- <kahaDB directory="${activemq.data}/kahadb"/> -->
	<jdbcPersistenceAdapter dataSource="#mysql-ds" createTablesOnStartup="true" /> 
</persistenceAdapter>

不要忘记mysql数据库依赖jar包 commons-dbcp commons-pool mysql-connector-java

LevelDB存储

LevelDB持久化性能高于KahaDB,虽然目前默认的持久化方式仍然是KahaDB。并且,在ActiveMQ 5.9版本提供了基于LevelDB和Zookeeper的数据复制方式,用于Master-slave方式的首选数据复制方案。但是在ActiveMQ官网对LevelDB的表述:LevelDB官方建议使用以及不再支持,推荐使用的是KahaDB

Memory 消息存储

顾名思义,基于内存的消息存储,就是消息存储在内存中。persistent=”false”,表示不设置持久化存储,直接存储到内存中,在broker标签处设置。存在的问题就是很容易丢失消息。内存频繁GC也会容易导致消息堆积问题。

JDBC Message store with ActiveMQ Journal

他是在集群环境下使用的策略。这种方式克服了JDBC Store的不足,JDBC存储每次消息过来,都需要去写库和读库。 ActiveMQ Journal,使用延迟存储数据到数据库,当消息来到时先缓存到文件中,延迟后才写到数据库中。
当消费者的消费速度能够及时跟上生产者消息的生产速度时,journal文件能够大大减少需要写入到DB中的消息。 举个例子,生产者生产了1000条消息,这1000条消息会保存到journal文件,如果消费者的消费速度很快的情况 下,在journal文件还没有同步到DB之前,消费者已经消费了90%的以上的消息,那么这个时候只需要同步剩余的 10%的消息到DB。如果消费者的消费速度很慢,这个时候journal文件可以使消息以批量方式写到DB。

我们可以在生产者端关闭持久化

MessageProducer producer = session.createProducer(queue);
// 默认是开启持久化的
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); // 设置为不持久化

执行流程

事物

session.commit(); 提交事物
session.rollback();回滚事物

如何开启事物?

在消息发送端

// 第一个参数设置为true表示开启事物
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

提交事物

TextMessage textMessage = session.createTextMessage("hello");
// 向目的地写入消息
producer.send(textMessage);
// 需要commit
session.commit();

回滚事物

比如我们有一个需求是需要方法A和方法B同时执行,并且两个方法都执行成功才算成功,如果一一个方法执行失败了,另一个方法就需要回滚数据。 比如网上购物的时候,我们购买商品付款,后台会扣我的款和扣除商品库存。

try {
	pay();
	minus();
} catch (Exception e) {
	session.rollback();
}

使用场景:

批量发送减少网络连接次数。

签收模式

签收代表接收端的session已收到消息的一次确认,反馈给broker ActiveMQ支持自动签收与手动签收

Session.AUTO_ACKNOWLEDGE

当客户端从receiver或onMessage成功返回时,Session自动签收客户端的这条消息的收条。

Session.CLIENT_ACKNOWLEDGE

客户端通过调用消息(Message)的acknowledge方法签收消息。在这种情况下,签收发生在Session层面:签收一个已经消费的消息会自动地签收这个Session所有已消费的收条。

Session.DUPS_OK_ACKNOWLEDGE

Session不必确保对传送消息的签收,这个模式可能会引起消息的重复,但是降低了Session的开销,所以只有客户端能容忍重复的消息,才可使用。