三种主从模式
主从方式 | 要求 | 性能 |
---|---|---|
共享文件系统 | 要求SAN网络共享存储 | |
JDBC主从方式 | 共享数据库 | 速度不如日志快 |
复制的LevelDB | zookeeper服务器 | 速度快 |
共享文件系统配置
多个broker争夺共享文件锁,取得文件锁的成为master
<persistenceAdapter>
<kahaDB directory="/sharedFileSystem/sharedBrokerData"/>
persistenceAdapter>
or
<persistenceAdapter>
<levelDB directory="/sharedFileSystem/sharedBrokerData"/>
persistenceAdapter>
or
<persistenceAdapter>
<amqPersistenceAdapter directory="/sharedFileSystem/sharedBrokerData"/>
persistenceAdapter>
<broker xmlns="http://activemq.apache.org/schema/core"
dataDirectory="/some/location"
brokerName="mmuserb2" useJmx="true" advisorySupport="false"
persistent="true" deleteAllMessagesOnStartup="false"
useShutdownHook="false" schedulerSupport="true">
JDBC主从方式
跟共享文件系统的方式类似,但是更可靠,速度上最慢
<beans>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<broker xmlns="http://activemq.apache.org/schema/core">
<destinationPolicy>
<policyMap><policyEntries>
<policyEntry topic="FOO.>">
<dispatchPolicy>
<strictOrderDispatchPolicy />
dispatchPolicy>
<subscriptionRecoveryPolicy>
<lastImageSubscriptionRecoveryPolicy />
subscriptionRecoveryPolicy>
policyEntry>
policyEntries>policyMap>
destinationPolicy>
<persistenceAdapter>
<jdbcPersistenceAdapter dataDirectory="${activemq.base}/activemq-data"/>
<jdbcPersistenceAdapter dataDirectory="activemq-data" dataSource="#oracle-ds"/>
persistenceAdapter>
<transportConnectors>
<transportConnector name="default" uri="tcp://localhost:61616"/>
transportConnectors>
broker>
<bean id="oracle-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:AMQDB"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
<property name="poolPreparedStatements" value="true"/>
bean>
beans>
可复制的LevelDB存储(zooKeeper主从模式推荐模式)
从一系列的broker中取出一个broker作为master,从master的broker同步更新的数据到所有的slave broker。对于N个的broker,同步复制(N/2 + 1)机器后,异步复制其他slave。
该模式下下面的配置应该是一样的
"broker" ... >
...
"activemq-data"
replicas="3"
bind="tcp://0.0.0.0:0"
zkAddress="zoo1.example.org:2181,zoo2.example.org:2181,zoo3.example.org:2181"
zkPassword="password"
zkPath="/activemq/leveldb-stores"
hostname="broker1.example.org"
/>
...
属性 | 默认值 | 注释 |
---|---|---|
replicas | 3 | (replicas/2 + 1)数量避免集群宕机 |
securityToken | 做复制时候的安全令牌 | |
zkAddress | 127.0.0.1:2181 | 使用逗号分隔一系列zk |
zkPassword | 连接到zk时候的密码 | |
zkPath | /default | 主从切换时候选举信息 |
zkSessionTimeout | 2s | 检测一个zk节点是否宕机 |
sync | quorum_mem | 使用逗号分隔的一系列值,local_mem,local_disk,remote_mem,remote_disk,quonum_mem,quonum_disk,如果结合多种同样的话,disk优先取,quorum_mem等价于local_mem,remote_mem,quorum_disk等价于local_disk,remote_disk |
每个节点都需要不同的
属性 | 默认值 | 注释 |
---|---|---|
bind | tcp://0.0.0.0:61619 | |
hostname | ||
weight | 1 | 权重 |
标准的LevelDB存储属性
属性 | 默认值 | 注释 |
---|---|---|
directory | LevelDB | 用于存储数据文件,不存在会自动创建 |
readThreads | 10 | read线程数量 |
logSize | 104857600(100MB) | 单个数据文件最大值 |
verifyChecksums | false | 校验数据文件 |
paranoidChecks | false | 存储error尽早抛出异常 |
indexFactory | org.fusesource.leveldbjni.JniDBFactory, org.iq80.leveldb.impl.Iq80DBFactory | LevelDB索引 |
indexMaxOpenFiles | 1000 | 索引能使用openfile数量 |
indexBlockRestartInterval | 16 | |
indexWriteBufferSize | 6291456(16MB) | 写入到磁盘前内存索引数据大小 |
indexBlockSize | 4096 | |
indexCacheSize | 268435456(256MB) | 用于缓存索引快的内存 |
indexCompression | snappy | 用于索引块的压缩策略,snappy或者none |
logCompression | none | 用于日志记录的压缩策略,snappy或者none |
5.4版本以后activemq推荐使用kahaDB作为默认的存储机制
kahadb在activemq的使用
<broker brokerName="broker">
<persistenceAdapter>
<kahaDB directory="activemq-data" journalMaxFileLength="32mb"/>
persistenceAdapter>
broker>
KahaDB 的属性
属性 | 默认值 | 注释 |
---|---|---|
archiveCorruptedIndex | false | true为归档毁坏的索引(不删除) |
archiveDataLogs | false | true表示把消息数据文件移动到归档目录(不删除) |
checkForCorruptJournalFiles | false | true表示在启动的时候检查毁坏文件并尝试恢复 |
checkpointInterval | 5000 | 单位毫秒 |
checksumJournalFiles | true | |
cleanupInterval | 30000 | 单位毫秒 |
compactAcksAfterNoGC | 10 | 设置过大会影响性能, |
compactAcksIgnoresStoreGrowth | false | |
concurrentStoreAndDispatchQueues | true | |
concurrentStoreAndDispatchTopics | false | 推荐设置为false |
directory | activemq-data | 数据和日志文件目录 |
directoryArchive | null | 消费后消息归档位置 |
enableAckCompaction | true | |
enableIndexWriteAsync | false | true表示异步更新索引 |
enableJournalDiskSyncs | true | 废,请看journalDiskSyncStrategy |
ignoreMissingJournalfiles | false | true表示忽略遗失的日志文件 |
indexCacheSize | 10000 | 在内存中缓存的索引页数量 |
indexDirectory | 不设置使用directory目录 | |
indexWriteBatchSize | 1000 | 批量写入的索引数量 |
journalDiskSyncInterval | 1000 | 单位毫秒,同步磁盘时间间隔 |
journalDiskSyncStrategy | always | always :periodic :never : |
journalMaxFileLength | 32mb | |
maxAsyncJobs | 10000 | 队列存储最大的异步消息,应该与MessageProducer的数量一样 |
preallocationScope | entire_journal_async | 预分配文件 |
preallocationStrategy | sparse_file | 如何预先分配文件,sparse_file,设置文件长度,os_kernel_copy,委托给操作系统,zeros不设置文件长度 |
storeOpenWireVersion | 11 | 5.9.0默认为6 |
网络模式
用于提供高可用,容灾的分布式队列和话题,客户端能够快速从宕机的broker切换到另外正常运作的broker。
<beans xmlns="http://activemq.org/config/1.0">
<broker brokerName="receiver" persistent="false" useJmx="false">
<networkConnectors>
<networkConnector uri="static:(tcp://localhost:62001)"/>
networkConnectors>
<persistenceAdapter>
<memoryPersistenceAdapter/>
persistenceAdapter>
<transportConnectors>
<transportConnector uri="tcp://localhost:62002"/>
transportConnectors>
broker>
beans>
<beans xmlns="http://activemq.org/config/1.0">
<broker name="sender" persistent="false" useJmx="false">
<networkConnectors>
<networkConnector uri="multicast://default"/>
networkConnectors>
<persistenceAdapter>
<memoryPersistenceAdapter/>
persistenceAdapter>
<transportConnectors>
<transportConnector uri="tcp://localhost:0" discoveryUri="multicast://default"/>
transportConnectors>
broker>
beans>
network Connector的配置形式如下
multicast://address:port?transportOptions
选项名称 | 默认值 | 描述 |
---|---|---|
group | default | 起一个唯一名字避免多播阻塞 |
minmumWireFormatVersion | 0 | 最小允许的wireFomat版本 |
trace | false | 记录传输的所有命令 |
useLocalHost | true | true表示本地机器命名为localhost |
datagramSize | 4 * 1024 | UDP包的大小 |
timeToLive | -1 | 数据包生存时间,设置大于1可以发送到其他的broker中 |
loopBackMode | false | 回路模式是否使用 |
wireFormat | default | 使用的WireFormat名字 |
wireFormat.* | 用于配置wireFormat |
使用默认值配置等价于multicast://239.255.2.3:6155
multicast://default
multicast://224.1.2.3:6255?group=mygroupnam
配置broker消息代理的信息
需要加上前缀wireFormat.
属性名 | 默认值 | 描述 |
---|---|---|
cacheEnabled | true | |
cacheSize | 1024 | 当使用缓存时候,缓存的数量 |
maxInactivityDuration | 30000 | 认为socket已经挂死的间隔时间然后主动杀死connection,为0表示不启用 |
maxInactivityDurationInitalDelay | 10000 | 检查时延 |
maxFrameSize | MAX_LONG | 允许的帧大小,配置以避免oom |
prefixPacketSize | true | |
stackTraceEnabled | ture | |
tcpNoDelayEnabled | true | |
tightEncodingEnabled | true |
<networkConnectors>
<networkConnector uri="static:(tcp://host1:61616,tcp://host2:61616,tcp://..)"/>
networkConnectors>
静态发现的属性
属性 | 默认值 | 描述 |
---|---|---|
initialReconnectDelay | initialReconnectDelay | 单位毫秒,useExponentialBackOff为false情况下,第一次尝试重连的时间间隔 |
maxReconnectDelay | 30000 | 单位毫秒,重连间隔时间 |
useExponentialBackOff | true | 每次重连失败都会增加重连时间间隔 |
backOffMultiplier | 2 | useExponentialBackOff每次增加时间 |
uri="static:(tcp://host1:61616,tcp://host2:61616)?maxReconnectDelay=5000&useExponentialBackOff=false"
建立网络桥接模式到一个broker以及她的slave中,使用masterslave来进行操作
<networkConnectors>
<networkConnector uri="masterslave:(tcp://host1:61616,tcp://host2:61616,tcp://..)"/>
networkConnectors>
URI的顺序是
The URIs are listed in order for: MASTER,SLAVE1,SLAVE2…SLAVE(thumbs down)
属性 | 默认值 | 描述 |
---|---|---|
name | bridge | network的名称,两个相同broker之间的不同networkConnector使用不同的名字 |
dynamicOnly | false | true表示仅当进行持久订阅的时候network才激活,默认在启动时候就激活 |
decreaseNetworkConsumerPriority | false | true表示消费者启动时候优先级为-5,经过一个跳的network连接消费者就减少1,false表示所有的消费者使用与本地消费者同样的优先级0 |
networkTTL | 1 | 消息和持久订阅能够穿越的broker数量 both message&consumer |
messageTTL | 1 | 消息能穿越的broker数量 |
consumerTTL | 1 | 消费者能够穿越的broker数量 |
conduitSubscriptions | true | 多个消费者消费消息被当作一个消费者 |
excludedDestinations | empty | 在这个名单中的队列将不会在网络桥接模式在传递 |
dynamicallyIncludedDestinations | empty | 可以在网络桥接模式的broker传播,empty表示不在excludedDestinations中的队列都可以传播 |
useVirtualDestSubs | false | true表示监听虚拟队列的公告消息 |
staticallyIncludedDestinations | empty | 会在网络桥接中传播,即便没有消费者刚兴趣 |
duplex | false | 是否全双工作模式,true同时用于生产和消费消息 |
prefetchSize | 1000 | 每次预取的数量 |
suppressDuplicateQueueSubscriptions | false | true表示网络桥接中两个broker复制消息将会被制止,比如,A,B,C三个broker使用multicast discovery,A上的消费者a会在产生B和C的broker上,此外,C上消费者也会连接到B,B消费者也会连接到C;为true的时候,BC之间的网络桥接将被禁止,因为她们已经订阅了Abroker上面的消息了,减少路由选择,用这种方法能够确定消除消息在死路由(卡住消息)网络中迁移的可能,networkTTL大于等于broker的数量时候需要这个属性进行调停。 |
bridgeTempDestinations | true | |
alwaysSyncSend | false | true表示不持久化的消息使用请求应答的模式,这个配置对持久和非持久的选择都一样 |
staticBridge | false | true表示broker不会动态响应新的消费者,将会使用staticallyIncludedDestinations创建新的需求订阅。 |
userName | null | 远程broker的用户名 |
password | null | 密码 |
可靠性
如果源broker是持久订阅或者持久队列,network会保持持久化
如果源broker是非持久化的,发生异常会导致线上消息丢失
消息时序
无法保证消息时序,存在消费者从一个broker迁移到另外一个broker的情况。
管道订阅(Conduit subscription)
activemq使用活跃的消费者在network中传递消息。broker复制了一份远程broker的订阅,然后路由传送给本地的客户端连接。如果是话题或者是超过一个的订阅,多个副本将会消息产生。导管就是为了预防副本消息在整个network中泛滥传播,这个默认运行情况,使得多个订阅在远程的broker上行为就如同单个订阅一样。
然而,复制的订阅是一个有用的特性如果我们仅仅使用队列模式开发,这样子负载均衡算法就能把消息均匀的分配,如果conduitSubscriptions=false的话,在network上的消费者将会均分消息。比如AB两个broker,B上有2个消息者,A只有1个消费者,A生产了30个消息,在conduitSubscriptions=true的情况下,15个发送到A消费者,15个发送到B的borker上,如果conduitSubscriptions=false,10个发送到A,10个发送到B1,10个发送到B2。
Duplex network connectors
broker1配置到broker2的netwokrConnector是单向,如果设置duplex=true的话就是双向的了,
同时可以配置在同一个broker中配置多个networkConnector增加吞吐量
<networkConnectors>
<networkConnector name="SYSTEM1" duplex="true" uri="static:(tcp://10.x.x.x:61616)">
<dynamicallyIncludedDestinations>
<topic physicalName="outgoing.System1" />
dynamicallyIncludedDestinations>
networkConnector>
<networkConnector name="SYSTEM2" duplex="true" uri="static:(tcp://10.x.x.x:61616)">
<dynamicallyIncludedDestinations>
<topic physicalName="outgoing.System2"/>
dynamicallyIncludedDestinations>
networkConnector>
networkConnectors>
导管订阅忽略了在本地broker的消费者的selector并把消息传送给远程的消费者。远程的broker的selector在消息分发之前被解析,这在多broker中会存在问题。可以想象消息被A产生被传送到B或者C等broker,但是消息并不能满足B,C等broker等selector的特征,为了避免这种情况,要开启conduitSubscription这个属性。
advisorySupport如果没有开启的话networkConnector可能会不正常的工作。
networkConnector严重依赖于advisory messages,消费者连接到远程的broker的话,会接收到所有关于advisory Message,在大规模情况下工作效果不会工作的很好。
只把特定队列的消息发送到远程broker。
<networkConnector uri="static:(tcp://host)">
<dynamicallyIncludedDestinations>
<queue physicalName="include.test.foo"/>
<topic physicalName="include.test.bar"/>
dynamicallyIncludedDestinations>
networkConnector>
在activemq的5.6版本或者更久远的需要增加前缀ActiveMQ.Advisory.Consumer.,配置demo如下
<networkConnector uri="static:(tcp://host)" destinationFilter="Queue.include.test.foo,ActiveMQ.Advisory.Consumer.Topic.include.test.bar">
<dynamicallyIncludedDestinations>
<queue physicalName="include.test.foo"/>
<topic physicalName="include.test.bar"/>
dynamicallyIncludedDestinations>
networkConnector>
用于保存broker不受远程broker的消费者的影响,或者把当前broker当做一个用于传送消息的代理,可以考虑配置成static network
<networkConnector uri="static:(tcp://host)" staticBridge="true">
<staticallyIncludedDestinations>
<queue physicalName="always.include.queue"/>
staticallyIncludedDestinations>
networkConnector>
staticBridge 参数在5.6版本以后使用,表示本地的broker不会从远程的broker订阅任何有关advisoryMessage,staticallyIncludedDestinations表示该队列或者是话题可以传播到远程的broker去,在5.6之前的版本不存在staticBridge的属性,我们可以destinationFilter 去监听没用到的advisory话题,类似下面的配置
<networkConnector uri="static:(tcp://host)" destinationFilter="NO_DESTINATION">
<staticallyIncludedDestinations>
<queue physicalName="always.include.queue"/>
staticallyIncludedDestinations>
networkConnector>
如果像上述配置的话,broker将会尝试监听在ActiveMQ.Advisory.Consumer.NO_DESTINATION的新的消费者,并且不受远程broker的消费者影响。
这里存在两个broker,LocalBroker配置的network如下
<networkConnector uri="static:(tcp://host)">
<dynamicallyIncludedDestinations>
<topic physicalName="include.bar"/>
dynamicallyIncludedDestinations>
networkConnector>
remote Broker配置了组合话题如下
<compositeTopic name="include.bar" forwardOnly="false">
<forwardTo>
<queue physicalName="include.bar.forward" />
forwardTo>
compositeTopic >
假设在remote Broker的include.bar.forward队列的存在一个消费者。往include.bar话题发送了一个消息的话,实际上将会被传送到
include.bar.forward队列并被消费掉。如果是往localBroker发布一个相同的话题,这个消息将不会传送到remoteBroker去。
消息之所以不会被传送是因为在include.bar.forward的消费者不能检测到localBroker的dynamicallyIncludedDestinations 列表,因为没有在原始队列(话题)include.bar的消费者,消息不会被传送到RemoteBroker。这里我们通过配置LocalBroker来监听虚拟目的地订阅来解决。
首先,当消费者的订阅符合虚拟目的地,我们需要配置remoteBroker发送advisoryMessage,在这个例子中,我们配置remoteBroker的useVirtualDestSubs 为true
<beans xmlns="http://activemq.org/config/1.0">
<broker name="remoteBroker" useVirtualDestSubs="true">
.....
broker>
beans>
同时,配置localBroker监听advisoryMessage设置useVirtualDestSubs 为true
<networkConnector uri="static:(tcp://host)" useVirtualDestSubs="true">
<dynamicallyIncludedDestinations>
<topic physicalName="include.bar"/>
dynamicallyIncludedDestinations>
networkConnector>
配置后在remoteBroker的队列include.bar.forward的消费者可以获取到从LocalBroker传送过来的include.bar的话题。
让我们重新考虑上面的组合情况,如果没有消费者在remoteBroker的队列上,
remoteBroker的组合队列配置如下
<compositeTopic name="include.bar" forwardOnly="false">
<forwardTo>
<queue physicalName="include.bar.forward" />
forwardTo>
compositeTopic >
组队话题配置在remoteBroker上,然后localBroker使用network连接到这个上面,即便我们开启了useVirtualDestSubs属性,消息也不会发送到没有消费者的remoteBroker上,这样子消息只会挤压在LocalBroker上,需要
设置remoteBroker的属性useVirtualDestSubsOnCreation=true,消息会发送到remoteBroker上,同时remoteBroker必须是
1到多个队列,拥有持久化的话题
<beans xmlns="http://activemq.org/config/1.0">
<broker name="remoteBroker" useVirtualDestSubs="true" useVirtualDestSubsOnCreation="true">
.....
broker>
beans>
LocalBroker
<networkConnector uri="static:(tcp://host)">
<dynamicallyIncludedDestinations>
<topic physicalName="VirtualTopic.>"/>
dynamicallyIncludedDestinations>
networkConnector>
RemoteBroker
<beans xmlns="http://activemq.org/config/1.0">
<broker name="remoteBroker" useVirtualDestSubs="true" >
.....
broker>
beans>
默认情况,消息是不允许回溯会他来自的broker,这样子就可以避免消息的传递成为一个环。偶然的情况下,存在需要消息重新回溯到原有队列。比如在这种情景,有两个broker,其中一个broker宕机后,消费者路由到另外一个broker中,然后宕机的broker被重启,但是没有消费者在宕机的broker上进行消费,所以宕机的broker重启后上挤压的消息没被消费掉,一个解决的方法是配置rebalanceClusterClients=true属性,另一种解决方法是允许消息回溯回他原来的broker。配置conditionalNetworkBridgeFilterFactory 标签的属性replayWhenNoConsumers=true。
如果是activemq版本小于5.9,需要关闭游标复制检测,enableAudit=false。conditionalNetworkBridgeFilterFactory 还可以调节消费速率限制。
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue="TEST.>" enableAudit="false">
<networkBridgeFilterFactory>
<conditionalNetworkBridgeFilterFactory replayWhenNoConsumers="true"/>
networkBridgeFilterFactory>
policyEntry>
policyEntries>
policyMap>
destinationPolicy>