ActiveMQ进阶3---ActiveMQ传输协议和持久化

9、ActiveMQ 的传输协议

9.1、简介

  • ActiveMQ支持的client-broker通讯协议有:TVP、NIO、UDP、SSL、Http(s)、VM。其中配置Transport Connector的文件在ActiveMQ安装目录的conf/activemq.xml中的标签之内。
  • activemq传输协议的官方文档:http://activemq.apache.org/configuring-version-5-transports.html
<transportConnectors>
    <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB -->
    <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
    <transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
    <transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
    <transportConnector name="mqtt" uri="mqtt://0.0.0.0:1883?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
    <transportConnector name="ws" uri="ws://0.0.0.0:61614?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
</transportConnectors>

在上文给出的配置信息中,URI描述信息的头部都是采用协议名称:例如

  • 描述amqp协议的监听端口时,采用的URI描述格式为“amqp://······”;

  • 描述Stomp协议的监听端口时,采用URI描述格式为“stomp://······”;

  • 唯独在进行openwire协议描述时,URI头却采用的“tcp://······”。这是因为ActiveMQ中默认的消息协议就是openwire。

9.2、支持的传输协议

​ 除了tcp和nio协议,其他的了解就行。各种协议有各自擅长该协议的中间件,工作中一般不会使用activemq去实现这些协议。如: mqtt是物联网专用协议,采用的中间件一般是mosquito。ws是websocket的协议,是和前端对接常用的,一般在java代码中内嵌一个基站(中间件)。stomp是邮箱使用的协议的,各大邮箱公司都有基站(中间件)。

注意:协议不同,我们的代码都会不同。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vYV3YG9N-1605320292664)(images04/image-20201112223535497.png)]

9.2.1、TCP 协议

  1. Transmission Control Protocol(TCP)是默认的。TCP的Client监听端口61616
  2. 在网络传输数据前,必须要先序列化数据,消息是通过一个叫wire protocol的来序列化成字节流。
  3. TCP连接的URI形式如:tcp://HostName:port?key=value&key=value,后面的参数是可选的。
  4. TCP传输的的优点:
    • TCP协议传输可靠性高,稳定性强
    • 高效率:字节流方式传递,效率很高
    • 有效性、可用性:应用广泛,支持任何平台
  5. 关于Transport协议的可选配置参数可以参考官网http://activemq.apache.org/tcp-transport-reference

9.2.2、NIO 协议

  1. New I/O API Protocol(NIO)
  2. NIO协议和TCP协议类似,但NIO更侧重于底层的访问操作。它允许开发人员对同一资源可有更多的client调用和服务器端有更多的负载。
  3. 适合使用NIO协议的场景:
    • 可能有大量的Client去连接到Broker上,一般情况下,大量的Client去连接Broker是被操作系统的线程所限制的。因此,NIO的实现比TCP需要更少的线程去运行,所以建议使用NIO协议。
    • 可能对于Broker有一个很迟钝的网络传输,NIO比TCP提供更好的性能。
  4. NIO连接的URI形式:nio://hostname:port?key=value&key=value
  5. 关于Transport协议的可选配置参数可以参考官网http://activemq.apache.org/configuring-version-5-transports.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DLaDqaqP-1605320292667)(images04/image-20201113091402114.png)]

9.2.3、AMQP 协议

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MckHyZY3-1605320292668)(images04/image-20201113091616488.png)]

9.2.4、STOMP 协议

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2VDoAq3K-1605320292670)(images04/image-20201113092426332.png)]

9.2.5、MQTT协议

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zm1AxQww-1605320292672)(images04/image-20201113092930082.png)]

9.3、NIO 协议案例

ActiveMQ这些协议传输的底层默认都是使用BIO网络的IO模型。只有当我们指定使用nio才使用NIO的IO模型。

(1) 修改配置文件activemq.xml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PPYddrof-1605320292674)(images04/image-20201113092347825.png)]

① 修改配置文件activemq.xml在 节点下添加如下内容:

② 修改完成后重启activemq: service activemq restart

③ 查看管理后台,可以看到页面多了nio

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vHhfoF0D-1605320292675)(images04/image-20201113093356633.png)]

(2)代码:只需要修改两行

public static final String ACTIVEMQ_URL = "nio://192.168.5.130:61618";
public static final String QUEUE_NAME = "nio_test";

9.4、NIO 协议增强案例

(1)目的:上面是Openwire协议传输底层使用NIO网络IO模型。 如何让其他协议传输底层也使用NIO网络IO模型呢?

(2)解决:使用auto关键字,使用"+"符号来为端口设置多种特性

修改配置文件activemq.xml

<transportConnector name="nio" uri="nio://0.0.0.0:61618?trace=true" />
<transportConnector name="auto+nio" uri="auto+nio://0.0.0.0:61608?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600&amp;org.apache.activemq.transport.nio.SelectorManager.corePoolSize=20&amp;org.apache.activemq.transport.nio.Se1ectorManager.maximumPoo1Size=50"/>

auto :针对所有的协议,会自动识别使用的是什么协议。

nio :使用NIO网络IO模型。

(3)代码

  1. 使用 nio 模型的 tcp 协议生产者和消费者

    public static final String ACTIVEMQ_URL = "tcp://192.168.5.130:61608";
    public static final String QUEUE_NAME = "anto-nio";
    
  2. 使用 nio 模型的 nio 协议生产者和消费者

    public static final String ACTIVEMQ_URL = "nio://192.168.5.130:61608";
    public static final String QUEUE_NAME = "anto-nio";
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T7DPnI2z-1605320292676)(images04/image-20201113104215400.png)]

10、ActiveMQ 的消息存储和持久化

10.1、介绍

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A0c8ZO52-1605320292677)(images04/image-20201113122757645.png)]

MQ高可用:事务、可持久、签收,是属于MQ自身特性,自带的。这里的持久化是外力,是外部插件。之前的持久化是MQ的外在表现,现在的持久是底层实现。

(2) 是什么:

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

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

说明:为了避免意外宕机以后丢失信息,需要做到重启后可以恢复消息队列,消息系统一半都会采用持久化机制。ActiveMQ的消息持久化机制有JDBC,AMQ,KahaDB和LevelDB,无论使用哪种持久化方式,消息的存储逻辑都是一致的。

  • 在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等。再试图将消息发给接收者,成功则将消息从存储中删除,失败则继续尝试发送。消息中心启动以后,要先检查指定的存储位置是否有未成功发送的消息,如果有,则会先把存储位置中的消息发出去。

10.2、存储机制

10.2.1、AMQ Message Store(了解)

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

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

10.2.2、LevelDB消息存储(了解)

这种文件系统是从ActiveMQ5.8之后引进的,它和KahaDB非常相似,也是基于文件的本地数据库储存形式,但是它提供比KahaDB更快的持久性。

但它不使用自定义B-Tree实现来索引预写日志,而是使用基于LevelDB的索引

默认配置如下:

<persistenceAdapter>
    <levelDB directory="activemq-data"/>
</persistenceAdapter>

10.2.3、kahaDB(默认)

(1)介绍

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

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

配置文件activemq.xml中,如下

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

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

(2)说明

kahaDB是目前默认的存储方式,可用于任何场景,提高了性能和恢复能力

消息存储使用一个事务日志和仅仅用一个索引文件来存储它所有地址

kahaDB是一个专门针对消息持久化的解决方案,它对典型的消息使用模式进行了优化

数据被追加到data logs中,当不再需要log文件中的数据的时候,log文件就会被丢弃

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HiTlS0b5-1605320292679)(images04/image-20201113132525620.png)]

kahaDB 的存储原理

kahaDB在消息保存目录中只有4类文件和一个lock,跟ActiveMQ的其他几种文件存储引擎相比就非常简洁了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iMg9Pc9M-1605320292680)(images04/image-20201113133726453.png)]

备注:正常情况kahadb文件夹中有5个文件,这个少了 db.free。

db-Number.log
KahaDB存储消息到预定义大小的数据记录文件中,文件命名为db-Number.log。当数据文件已满时,一个新的文件会随之创建,number数字也会随之递增,它随着消息数量的增多,如每32M一个文件,文件名按照数字进行编号,如db-1.log、db-2.log、db-3.log…。当不再有引用到数据文件中的任何消息时,文件会被删除或归档。

db.data
该文件包含了持久化的BTree索引,索引了消息数据记录中的消息,它是消息的索引文件,本质上是B-Tree(B树),使用B-Tree作为索引指向db-Number.log里面存储的消息。

db.free
当前db.data文件里哪些页面是空闲的,文件具体内容是所有空闲页的ID。

db.redo
用来进行消息恢复,如果KahaDB消息存储在强制退出后启动,用于恢复BTree索引。

lock
文件锁,表示当前获得kahadb读写权限的broker。

10.2.4、JDBC 消息存储

(1)原理图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SdwTAA98-1605320292681)(images04/image-20201113141025202.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fnFTqy3R-1605320292682)(images04/image-20201113142115688.png)]

(3)jdbcPersistenceAdapter配置

在 /myactiveMQ/apache-activemq-5.14.3/conf 路径下修改activemq.xml,将原先默认的kahaDB持久化配置替换为 jdbcPersistenceAdapter配置,即:

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

改为

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

dataSource:指定要引用的持久化数据库的bean名称,
createTablesOnStartup:是否在启动的时候创建数据表,默认值是true。这样每次启动都会去创建数据表了,一般是第一次启动的时候设置为true,之后改为false。

(4)数据库连接池配置

即将连接池配置放到 activemq.xml 文件的 </ broker> 与 < import resource=“jetty.xml”> 中间。配置如下:

<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.5.1:3306/activemq?relaxAutoCommit=true"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    <property name="poolPreparedStatements" value="true"/>
</bean>

说明:

  1. 这里用的是官网指定的数据库驱动(dbcp2),所以在步骤一只导入了 mysql-connector-java-5.1.48.jar 驱动包,若是公司用其他的数据库驱动,如c3p0等,则还需要导入对应的驱动包;
  2. bean中的 id值(mysql-ds) 要与 上一步配置中的dataSource的值一致,否则activemq无法启动成功。

(5)建表 SQL 和说明

重启activemq。会自动生成如下3张表。如果没有自动生成,需要我们手动执行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);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JfPQNATy-1605320292683)(images04/image-20201113144924259.png)]

三张表的说明:

  1. 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,数值越大优先级越高
    
  2. ACTIVEMQ_ACKS:用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存

    CONTAINER:消息的Destination
    
    SUB_DEST:如果是使用Static集群,这个字段会有集群其他系统的信息
    
    CLIENT_ID:每个订阅者都必须有一个唯一的客户端ID用以区分
    
    SUB_NAME:订阅者名称
    
    SELECTOR:选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性AND和OR操作
    
    LAST_ACKED_ID:记录消费过的消息的ID。
    
  3. ACTIVEMQ_LOCK:用于记录哪个Broker是当前的Master Broker

在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,其他的只能作为备份等待Master Broker不可用,才可能成为下一个Master Broker。

(6)queue 验证和数据表变化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GramuR04-1605320292684)(images04/image-20201113145927081.png)]

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

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

我们使用queue模式持久化,发布3条消息后,发现ACTIVEMQ_MSGS数据表多了3条数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysaSN4rE-1605320292685)(images04/image-20201113150057308.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UkqyeFf0-1605320292686)(images04/image-20201113151806255.png)]

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

(7)topic 验证和数据表变化

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bLBCFA74-1605320292687)(images04/image-20201113153848478.png)]

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

我们启动持久化生产者发布3个数据,ACTIVEMQ_MSGS数据表新增3条数据,消费者消费所有的数据后,ACTIVEMQ_MSGS数据表的数据并没有消失。持久化topic的消息不管是否被消费,是否有消费者,产生的数据永远都存在,且只存储一条。这个是要注意的,持久化的topic大量数据后可能导致性能下降。这里就像公总号一样,消费者消费完后,消息还会保留。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iAIxsGUn-1605320292688)(images04/image-20201113154034942.png)]

(8)小总结

  • 如果是queue: 当DeliveryMode设置为NON_PERSISTENCE时,消息被保存在内存中;当DeliveryMode设置为PERSISTENCE时,消息保存在broker的相应的文件或者数据库中。 而且点对点类型中消息一旦被Consumer消费就从broker中删除

  • 如果是Topic: 一般是先启动消费订阅然后再生产的情况下会将消息保存到activemq_acks. 而且发布者和消费者都会在数据库中有记录,不会自动删除

10.2.5、开发遇到的坑

在配置关系型数据库作为ActiveMQ的持久化存储方案时,有些位置需要注意:

  1. 数据库jar包:记得需要使用到的相关jar文件放置到ActiveMQ安装路径下的lib目录。mysql-jdbc驱动的jar包和对应的数据库连接池jar包。
  2. createTableOnStatup属性:在jdbcPersistenceAdapter标签中设置了createTableOnStatup属性为true时,在第一次驱动ActiveMQ时,ActiveMQ服务节点会自动创建所需要的数据表。启动完后可以去掉这个属性,或者更改createTableOnStartup属性为false。
  3. 下划线坑爹:“java.lang.lllegalStateException.BeanFactory not initialized or already closed” 这是因为操作系统的机器名中有“_”符号。请更改机器名并重启后即可解决问题。

10.2.6、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性能要高。

(2)配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vSjesWVr-1605320292689)(images04/image-20201113185011182.png)]

10.3、ActiveMQ持久化机制小总结

① 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基于第三方数据库
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值