针对单机而言,不管是多么好的机房环境都是不能保证服务100%可用的,只有我们在配置集群的情况下才能做到多个9的保证,消息中间件也是一样,虽然我们可以保证已经存在的数据尽量不丢失,但是在单机的情况下我们是不能保证服务的一直可用,也就可能导致后面的数据会丢失掉,在activemq的消息中间件主从集群中,它的数据的保存是放在公共的外部文件中,并不是存放在activemq节点对应每一台机器上面,即采用共享存储来实现文件的共享,根据官网https://activemq.apache.org/masterslave的介绍它有3种存储方式:共享文件系统、JDBC和leveldb的方式,其中leveldb采用的zookeeper方式,但是官方已经不再支持了。我们大概看下主从集群结构图
通过上面这个图我们可以看出它是用数据库来实现共享存储的,有1个master节点和2个slave节点,通过master节点对外提供服务,它的主要原理如下:
a.多个Broker实例共享存储
b.多个Broker通过抢独占锁来成为Master
c.Master节点对外提供服务,Slave节点暂停等待独占锁
d.Master节点故障,非持久化消息将丢失,所以一般要用持久化消息。
e.客户端以多Broker故障恢复方式进行连接
failover:(tcp://broker1:61616,tcp://broker2:61616,tcp://broker3:61616)
当broker1节点故障和恢复时的结构图如下
broker1节点故障时将会随机选举一个节点为master节点对外提供服务,broker1节点恢复后就只能作为slave节点使用了。那么在文件系统共享或者jdbc中它们是如何实现主从节点控制的呢?
a.文件系统共享是通过共享存储目录下的lock文件实现独占锁,官网参考https://activemq.apache.org/shared-file-system-master-slave,因为我实现的是文件共享,所以配置如下
<broker ...>
...
<!--<persistenceAdapter>
<kahaDB directory="D:\software\apache-activemq-5.15.10\data\masterslave"/>
<jdbcPersistenceAdapter dataSource="#mysql-ds" />
</persistenceAdapter>-->
<persistenceAdapter>
<mKahaDB directory="D:\software\apache-activemq-5.15.10\data\masterslave">
<filteredPersistenceAdapters>
<!-- match all queues-->
<filteredKahaDB queue=">">
<usage>
<storeUsage limit="1g" />
</usage>
<persistenceAdapter>
<kahaDB journalMaxFileLength="32mb" directory="D:\software\apache-activemq-5.15.10\data\masterslave\inner"/>
</persistenceAdapter>
</filteredKahaDB>
<!-- match all destinations -->
<filteredKahaDB>
<persistenceAdapter>
<!--5.14.0以后journalDiskSyncStrategy代替enableJournalDiskSyncs,可选值为always、never-->
<kahaDB journalDiskSyncStrategy="never" directory="D:\software\apache-activemq-5.15.10\data\masterslave\inner"/>
</persistenceAdapter>
</filteredKahaDB>
</filteredPersistenceAdapters>
</mKahaDB>
</persistenceAdapter>
...
</<broker>
b.jdbc共享是通过在activemq_lock表记录独占锁实现的。官网参考https://activemq.apache.org/jdbc-master-slave。
需要注意的是无论是文件系统共享还是jdbc共享,都需要配置相同的directory或者dataSource路径,否则是不能实现主从集群的。
下面主要说明下failover协议对应的一些配置,它是用来提供主从集群的协议,官网介绍在http://activemq.apache.org/failover-transport-reference.html,它有带参和不带参的2种配置方式,如下:
failover:(uri1,...,uriN)?transportOptions&nestedURIOptions
failover:uri1,...,uriN
接下来看下它的可用参数:
1.backup:默认值false,用作初始化并保持第二个传输连接——用以启用快速故障转移。
2.initialReconnectDelay:默认值10ms,用作在第一次重新连接尝试之前的延迟。
3.maxCacheSize:默认值131072字节,跟踪消息的缓存大小,仅当trackMessages为true时才启用。
4.maxReconnectAttempts:默认值是5.6及以后是-1,5.6之前是0,5.6版本及以后的-1表示一直尝试重连,0表示禁止重连,5.6版本之前的0表示一直尝试重连。配置的值大于0时表示在返回错误给客户端时重连的次数。
5.maxReconnectDelay:默认值30000ms,第二次和随后的重新连接尝试之间的最大延迟。
6.nested.*:默认值为null,它是5.9版本以后才有的,用于指定所有的url连接的公共配置,通过它配置的参数,所有的URI地址上面都会被自动扩充。
7.randomize:默认值为true,用于表示随机的获取连接地址中的一个用于重连。
8.reconnectDelayExponent:默认值2.0,用于在指数回退尝试中使用的指数。
9.reconnectSupported:默认值是true,用于确定客户端是否应使用重新连接来响应代理ConnectionControl事件,参考activemq.xml中配置的rebalanceClusterClients参数。
10.startupMaxReconnectAttempts:默认值-1,值-1表示在启动时连接尝试的次数应该是无限的。>=0的值表示在启动时将进行的重新连接尝试的次数,在此之后,当客户机进行后续的重新连接尝试时,将向客户机发回错误。注意:一旦成功连接,maxreconnecttries选项就会起作用。
11.timeout:默认值-1。用于设置发送操作的超时时间在不中断重新连接进程时,单位ms。
12.trackMessages:默认值false。用于保存将在重新连接时刷新到broker节点的动态消息缓存。配置第3点使用。
13.updateURIsSupported:默认值为true。从5.4版本开始用于确定客户端是否应该接受broker节点对其已知uri列表的更新。
14.updateURIsURL:默认值为null。从5.4版本开始用于表示一个包含逗号分隔的uri列表的文本文件的URL(或本地文件的路径),在失败的情况下用于重新连接,形如updateURIsURL=file:/D:/software/apache-activemq-5.15.10/data/connectUrl.txt。
15.useExponentialBackOff:默认值为true。用于表示如果为true,则在重新连接尝试之间使用指数回退。结合第8点使用。
16.warnAfterReconnectAttempts:默认值10。是5.10版本开始使用,用于表示值>0指定在记录警告之前重新连接的尝试次数。记录的警告表明没有当前连接,但正在尝试重新连接。值<=0禁止记录关于重新连接尝试的警告。
我们需要注意的是,失败重试机制是提供了默认的事务机制,在简单场景下它是可以保证事务的,但是如果当有许多连接和使用者时,这并不总是正确的保证事务,由于过时的未完成确认的消息会干扰新发送的消息,因此无法保证重新发送消息,这也就可能导致未确认的消息。从ActiveMQ 5.3.1版本开始消息是被跟踪,如果未完成的消息在故障转移后没有重新交付,事务将无法提交,如果提交失败,则抛出“TransactionRolledBackException”,那么事务将导致回滚,以便应用程序可以重新接收这些消息。
我们还可以在broker节点端进行配置针对failover协议,示例如下:
<broker>
...
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616" updateClusterClients="true" updateClusterFilter=".\*A.\*,.\*B.\*"/>
</<transportConnectors>
...
</broker>
看下可以配置的参数如下:
1.updateClusterClients:默认值为false,如果为true,则将有关代理集群拓扑结构更改的信息传递给连接的客户端。
2.rebalanceClusterClients:默认值为false,如果为true,当一个新的broker节点加入broker节点集群时,连接的客户端将被broker集群重新平衡(注意:priorityBackup=true可以覆盖)。
3.updateClusterClientsOnRemove:默认值为false,如果为true,则在从网络中删除一个cluster时更新客户机。将其作为单独的选项允许在新的broker节点加入时更新客户机,但在broker节点丢失时不更新客户机。
4.updateClusterFilter:默认值为null,逗号分隔的正则表达式筛选器列表,用于匹配要指定为客户端故障转移集群一部分的broker节点的broker节点名称。
当配置好以上的参数后,就可以直接在客户端配置一个已是master节点的连接地址了,如下
failover:(tcp://primary:61616)
如果主从集群中当前master节点不可用时,它会连接到其他已成为master节点的broker节点上。但是很遗憾的是经过我的测试,在broker节点配置failover协议的这个功能是无法使用的,不管是配合客户端使用明确的第9和13点两次参数功能还是单独使用以上配置都没有起到该功能所说的效果。如果使用连接参数的第14点的配置只在客户端配置一个broker的master节点是可以使用的。
从5.6版本开始,我们可以同时提供local和remote的broker节点进行连接,但是我们可能需要首先使用的是local的broker节点,如果local的broker节点不失效了,那么才会去连接remote的broker节点,我们可以进行指定priorityBackup参数为true来进行控制,如下配置:
failover:(tcp://local:61616,tcp://remote:61616)?randomize=false&priorityBackup=true
默认情况下只考虑第一个URI的优先级,因此只有一个local节点就可以使用了,但是有的时候需要配置多个local节点,那么可以进行下面的配置:
failover:(tcp://local1:61616,tcp://local2:61616,tcp://remote:61616)?randomize=false&priorityBackup=true&priorityURIs=tcp://local1:61616,tcp://local2:61616