Day176.ActiveMQ的消息储存和持久化 -ActiveMQ

ActiveMQ

ActiveMQ的消息储存和持久化

1.1 介绍

(1) 此处持久化和之前的持久化的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4pIOcJO1-1611736412762)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127111558708.png)]

MQ高可用:事务、可持久、签收,是属于MQ自身特性,自带的。这里的持久化是外力,是外部插件

之前讲的持久化是MQ的外在表现,现在讲的的持久是是底层实现。

通过独立的外部插件,如:文件储存、数据库等来做持久化备份,而不是单单储存在MQ数据库本身,加强了信息的高可用

MQ如百度云盘,外部插件如本地硬盘

(2)是什么:

官网文档:http://activemq.apache.org/persistence

持久化是什么?一句话就是:ActiveMQ宕机了,消息不会丢失的机制。

说明:为了避免意外宕机以后丢失信息,需要做到重启后可以恢复消息队列,消息系统一半都会采用持久化机制。ActiveMQ的消息持久化机制有JDBC,AMQ,KahaDB和LevelDB,无论使用哪种持久化方式,消息的存储逻辑都是一致的。就是在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等。再试图将消息发给接收者,成功则将消息从存储中删除,失败则继续尝试尝试发送。消息中心启动以后,要先检查指定的存储位置是否有未成功发送的消息,如果有,则会先把存储位置中的消息发出去。


1.2 ActiveMQ有哪些持久化机制

(1)AMQ Message Store

基于文件的存储机制,是以前的默认机制,现在不再使用。

AMQ是一种文件存储形式,它具有写入速度快和容易恢复的特点。消息存储再一个个文件中文件的默认大小为32M,当一个文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。AMQ适用于ActiveMQ5.3之前的版本

(2)kahaDB

现在默认的。下面我们再详细介绍。

(3)JDBC消息存储

下面我们再详细介绍。

(4)LevelDB消息存储

过于新兴的技术,现在有些不确定。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NantJCXr-1611736412765)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127113948374.png)]

(5)JDBC Message Store with ActiveMQ Journal

下面我们再详细介绍。


1.3 kahaDB消息存储

(1)介绍

基于日志文件,从ActiveMQ5.4(含)开始默认的持久化插件。

官网文档:http://activemq.aache.org/kahadb

官网上还有一些其他配置参数。

配置文件activemq.xml中,如下:↓

<persistenceAdapter>
    <kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>

日志文件的存储目录在:%activemq安装目录%/data/kahadb

(2)说明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j3kcctIY-1611736412768)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127122627779.png)]

(3)KahaDB的存储原理

.log 存 文件

.data 存 索引

在这里插入图片描述


1.4 JDBC消息存储

1.4.1 设置步骤

(1)原理图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yskMLkxe-1611736412777)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127125450254.png)]

(2)添加mysql数据库的驱动包到lib文件夹

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uCEXRVXT-1611736412778)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127132147317.png)]

(3)jdbcPersistenceAdapter配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tzVFIVvs-1611736412780)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127132157379.png)]

修改配置文件activemq.xml。将之前的替换为jdbc的配置。如下:

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

(4)数据库连接池配置

需要我们准备一个mysql数据库,并创建一个名为activemq的数据库。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u9N0VM1i-1611736412783)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127132228150.png)]

< /broker>标签和< import>标签之间插入数据库连接池配置:↓↓↓

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z0j25Mp2-1611736412785)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127132055195.png)]

具体操作如下:

    </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://mysql数据库URL/activemq?relaxAutoCommit=true"/>
        <property name="username" value="mysql数据库用户名"/>
        <property name="password" value="mysql数据库密码"/>
        <property name="poolPreparedStatements" value="true"/>
    </bean>

    <import resource="jetty.xml"/>

之后需要建一个数据库,名为activemq。新建的数据库要采用latin1 或者ASCII编码

https://blog.csdn.net/JeremyJiaming/article/details/88734762

默认是的dbcp数据库连接池,如果要换成其他数据库连接池,需要将该连接池jar包,也放到lib目录下。

然后将class引用修改成你导入的连接池jar包的DataSource


(5)建库SQL和创表说明

重启activemq。会自动生成如下3张表。如果没有自动生成,需要我们手动执行SQL。

我个人建议要自动生成,我在操作过程中查看日志文件,发现了不少问题,最终解决了这些问题后,是能够自动生成的。如果不能自动生成说明你的操作有问题。如果实在不行,下面是手动建表的SQL:

手动建表的代码sql代码:

-- auto-generated definition
create table ACTIVEMQ_ACKS
(
    CONTAINER     varchar(250)     not null comment '消息的Destination',
    SUB_DEST      varchar(250)     null comment '如果使用的是Static集群,这个字段会有集群其他系统的信息',
    CLIENT_ID     varchar(250)     not null comment '每个订阅者都必须有一个唯一的客户端ID用以区分',
    SUB_NAME      varchar(250)     not null comment '订阅者名称',
    SELECTOR      varchar(250)     null comment '选择器,可以选择只消费满足条件的消息,条件可以用自定义属性实现,可支持多属性AND和OR操作',
    LAST_ACKED_ID bigint           null comment '记录消费过消息的ID',
    PRIORITY      bigint default 5 not null comment '优先级,默认5',
    XID           varchar(250)     null,
    primary key (CONTAINER, CLIENT_ID, SUB_NAME, PRIORITY)
)
    comment '用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存';

create index ACTIVEMQ_ACKS_XIDX
    on ACTIVEMQ_ACKS (XID);

 
-- auto-generated definition
create table ACTIVEMQ_LOCK
(
    ID          bigint       not null
        primary key,
    TIME        bigint       null,
    BROKER_NAME varchar(250) null
);

-- auto-generated definition
create table ACTIVEMQ_MSGS
(
    ID         bigint       not null
        primary key,
    CONTAINER  varchar(250) not null,
    MSGID_PROD varchar(250) null,
    MSGID_SEQ  bigint       null,
    EXPIRATION bigint       null,
    MSG        blob         null,
    PRIORITY   bigint       null,
    XID        varchar(250) null
);

create index ACTIVEMQ_MSGS_CIDX
    on ACTIVEMQ_MSGS (CONTAINER);

create index ACTIVEMQ_MSGS_EIDX
    on ACTIVEMQ_MSGS (EXPIRATION);

create index ACTIVEMQ_MSGS_MIDX
    on ACTIVEMQ_MSGS (MSGID_PROD, MSGID_SEQ);

create index ACTIVEMQ_MSGS_PIDX
    on ACTIVEMQ_MSGS (PRIORITY);

create index ACTIVEMQ_MSGS_XIDX
    on ACTIVEMQ_MSGS (XID);

ACTIVEMQ_MSGS数据表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SEEfVHLF-1611736412787)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127141217847.png)]

ACTIVEMQ_ACKS数据表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQi0KWcA-1611736412789)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127141232140.png)]

ACTIVEMQ_LOCK数据表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WOnLYBma-1611736412790)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127141247803.png)]


阿昌遇到的问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BAimwFky-1611736412792)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127141400789.png)]

根据提示说createTableOnStartup不能出现在↓中

<persistenceAdapter>  
      <jdbcPersistenceAdapter dataSource="#mysql-ds" createTableOnStartup="true"/> 
</persistenceAdapter>

↓↓↓除去后

<persistenceAdapter>  
      <jdbcPersistenceAdapter dataSource="#mysql-ds"/> 
</persistenceAdapter>

再一次出现问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ksLqQsAr-1611736412793)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127141533231.png)]

网上查找一了波,发现需要在persistenceAdapter中添加useDatabaseLock="false"

<persistenceAdapter>
	<jdbcPersistenceAdapter dataSource="#mysql-ds" useDatabaseLock="false" />
</persistenceAdapter>

↓↓↓过了一会再去启动activemq

发现启动成功

↓↓↓登录前台控制网页http://192.168.109.101:8161/

发现登录不了

查看问题还是如上

↓↓↓过了一会再去重启activemq

可以正常访问

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F4oXfYZf-1611736412794)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127141726739.png)]

查看mysql数据库是否自动生成表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EO3qBBpN-1611736412796)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127141903484.png)]

自动生成,成功


1.4.2 queue验证和数据表变化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EzS243ge-1611736412798)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127144636942.png)]

//设置持久化
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
===================================================================
//设置不持久化
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

queue模式,非持久化不会将消息持久化到数据库。↑

queue模式,持久化会将消息持久化数据库。↑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SdZqGDLy-1611736412801)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127144649232.png)]

启动消费者,消费了所有的消息后,发现数据表的数据消失了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sixG5dYl-1611736412803)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127144723932.png)]

queue模式非持久化,不会持久化消息到数据表。

阿昌遇到的问题:↓

全部工作完成之后,在启动生产者代码后报错,查看日志↓

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yme0RNFN-1611736412805)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127144830011.png)]

这是因为,mysql默认的binlog_format是STATEMENT,而在READ COMMITTED或READ UNCOMMITTED隔离级别下,innodb只能使用的binlog_format是ROW。

而在ActiveMQ的store JDBC实现中(TransactionContext),为了提高并发性能,使用的是READ UNCOMMITTED:

解决方法

①在mysql里设置binlog_format=row,此时binlog会增大,但是一般来说对数据复制支持的更好,建议单机高性能环境下使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-58JWFcRc-1611736412806)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127144930782.png)]

②在activemq.xml的jdbcPersistenceAdapter里配置transactionIsolation="4"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZRKCcU2X-1611736412808)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127145032059.png)]


1.4.3 topic验证和说明

先启动一下持久化topic的消费者。看到ACTIVEMQ_ACKS数据表多了一条消息。

生产者代码

public class JmsConsumer_topic_Persist_jdbc {
    
    
    public static final String ACTIVEMQ_URL = "tcp://192.168.109.101:61616";
    public static final String TOPIC_NAME = "topic-jdbc01";

    public static void main(String[] args) throws JMSException, IOException {
    
    
        ActiveMQConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        Connection conn = mqConnectionFactory.createConnection();
        conn.setClientID("marrry");
        Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Topic topic = session.createTopic(TOPIC_NAME);
        TopicSubscriber subscriber = session.createDurableSubscriber(topic, "remark。。。");
        conn.start();
        Message receive = subscriber.receive();
        while (null != receive) {
    
    
            TextMessage textMessage = (TextMessage) receive;
            String messageText = textMessage.getText();
            System.out.println("收到持久化 topic: " + messageText);
            receive = subscriber.receive();
        }
        conn.close();
        session.close();

    }

}

消费者代码

public class JmsProducer_topic_Persist_jdbc {
    
    

    public static final String ACTIVEMQ_URL = "tcp://192.168.109.101:61616";
    public static final String TOPIC_NAME = "topic-jdbc01";

    public static void main(String[] args) throws JMSException {
    
    

        ActiveMQConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        Connection conn = mqConnectionFactory.createConnection();

        Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Topic topic = session.createTopic(TOPIC_NAME);
        MessageProducer messageProducer = session.createProducer(topic);
=======================================================================
        //会话设置持久化
        messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
=======================================================================
        // 设置持久化topic之后再,启动连接
        conn.start();
        for (int i = 1; i <= 3; i++) {
    
    
            TextMessage textMessage = session.createTextMessage("TOPIC_NAME-jdbc01--" + i);
            messageProducer.send(textMessage);
        }
        messageProducer.close();
        session.close();
        conn.close();


        System.out.println("===TOPIC_NAME=====消息发布到MQ完成===========");
    }

}

运行以上消费者代码ACTIVEMQ_ACKS数据表,多了一个消费者的身份信息。一条记录代表:一个持久化topic消费者

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BJO5qZJZ-1611736412809)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127150854918.png)]

我们启动持久化生产者发布3个数据,ACTIVEMQ_MSGS数据表新增3条数据,消费者消费所有的数据后,ACTIVEMQ_MSGS数据表的数据并没有消失。持久化topic的消息不管是否被消费,是否有消费者,产生的数据永远都存在,且只存储一条。

这个是要注意的,持久化的topic大量数据后可能导致性能下降。这里就像公众号一样,消费者消费完后,消息还会保留。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jXXoAM1B-1611736412811)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127151015275.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yDfMYZOG-1611736412813)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127151233848.png)]


14.4 总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vB3zcoOh-1611736412814)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127152438073.png)]

开发中的坑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ee13VBqJ-1611736412816)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127152448000.png)]


1.5 JDBC Message Store with ActiveMQ Journal

(1)说明

这种方式克服了JDBC Store的不足,JDBC每次消息过来,都需要去写库读库。ActiveMQ Journal,使用高速缓存写入技术,大大提高了性能。当消费者的速度能够及时跟上生产者消息的生产速度时,journal文件能够大大减少需要写入到DB中的消息。

举个例子:生产者生产了1000条消息,这1000条消息会保存到journal文件,如果消费者的消费速度很快的情况下,在journal文件还没有同步到DB之前,消费者已经消费了90%的以上消息,那么这个时候只需要同步剩余的10%的消息到DB。如果消费者的速度很慢,这个时候journal文件可以使消息以批量方式写到DB。

为了高性能,这种方式使用日志文件存储+数据库存储。先将消息持久到日志文件,等待一段时间再将未消费的消息持久到数据库。该方式要比JDBC性能要高。

在mysql和activemq中加了一层高速缓存

(2)配置

下面是基于上面JDBC配置,再做一点修改:

<persistenceFactory>
    <journalPersistenceAdapterFactory
    journalLogFiles="4"
    journalLogFileSize="32768"
    useJournal="true"
    useQuickJournal="true"
    dataSource="#mysql-ds"
    dataDirectory="activemq-data" />
</persistenceFactory>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j5NZfSPc-1611736412817)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127152740381.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zEtDranb-1611736412819)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127152745264.png)]

阿昌遇到的问题

添加上了上面的配置内容后,访问8161前台,被拒绝

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AD78ROGG-1611736412821)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127161545676.png)]

解决方法:跟前面的一样在,useDatabaseLock="false"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7NU6SOEa-1611736412823)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210127161707072.png)]

1.6 总结

① jdbc效率低,kahaDB效率高,jdbc+Journal效率较高。

② 持久化消息主要指的是:MQ所在服务器宕机了消息不会丢试的机制。

③ 持久化机制演变的过程:

从最初的AMQ Message Store方案到ActiveMQ V4版本退出的High Performance Journal(高性能事务支持)附件,并且同步推出了关于关系型数据库的存储方案。ActiveMQ5.3版本又推出了对KahaDB的支持(5.4版本后被作为默认的持久化方案),后来ActiveMQ 5.8版本开始支持LevelDB,到现在5.9提供了标准的Zookeeper+LevelDB集群化方案。

④ ActiveMQ消息持久化机制有:

AMQ 基于日志文件
KahaDB 基于日志文件,从ActiveMQ5.4开始默认使用
JDBC 基于第三方数据库
Replicated LevelDB Store 从5.9开始提供了LevelDB和Zookeeper的数据复制方法,用于Master-slave方式的首选数据复制方案。

感谢尚硅谷

猜你喜欢

转载自blog.csdn.net/qq_43284469/article/details/113252812