原文:
https://blog.csdn.net/zuolj/article/details/53116439
单纯根据《ActiveMQ In Action(Manning-2011)》一书介绍的总结,部分介绍可能已经和官网不一。
一、ActiveMQ的高可用性
ActiveMQ使用master-slave模式实现高可用性,提供两种实现主从模式的配置:shared nothing、shared storage(a relational database and a shared file system)
1.shared nothing master-slave
每一个broker(包括master和slave)都有自己的消息存储区,这是最简单的高可用性实现的办法。
master复制所有的消息指令给slave,复制的动作发生在master回复client消息已接收之前。
slave broker会在启动的时候去连接master,所以理想上,master broker应该先启动,slave broker 不会打开任何transports,也就是说,slave broker不接收任何client请求和网络连接,除非master挂掉。slave通过检测它与master之间的连接失败而判定master挂掉。
shared nothing master-slave模式的处理过程:当一个生产者发送一个持久化消息到master之后,master会复制该消息给slave,再返回接收应答给生产者,生产者才能发送下一个消息。
当master broker挂掉后,slave有两个选择:
1.关掉自己,因此,它只会保存master的状态。
2.打开transports并且初始化所有的network connections,因此,该slave自动成为新的master。
如果slave broker成为新的master broker,所有的client可以通过failover机制去连接上新的master。在默认的client连接中,failover传输机制皆可以连接到master和slave:
failover://(tcp://masterhost:61616,tcp://slavehost:61616)?randomize=false
不过,使用shared nothing master-slave也有限制,如果client先连上master进行工作,而slave还没与master进行连接,master挂掉,消息很可能会丢失。ActiveMQ提供了一个waitForSlave属性去设置master broker,强制master如果还没与slave建立好连接,那么不会接受任何client的连接,另一个限制是,一个master只允许有一个slave。
配置shared nothing master-slave
配置一个broker成为slave很简单,配置一个masterConnector service:
<services>
<!--
remoteURI:master broker的监听地址
userName:Optional,如果master需要身份验证
password:Optional,如果master需要身份验证
-->
<masterConnector remoteURI="tcp://remotehost:62001" userName="" password=""/>
</services>
2.shared storage master-slave
share nothing master-slave模式下,每一个broker都独自维护自己的storage,而shared storage master-slave模式允许多个broker共享存储,但同一个时刻只有一个broker是存活的。shared storage master-slave的好处在于,它确保了当master挂掉之后,无需手动干预去保持应用的完整性,另一个好处是,slave的数量不再有所限制。
share nothing master-slave模式的配置有两种:a relational database和file system-based storage.
shared database master-slave
当一个ActiveMQ broker使用关系型数据库时,它持有表的锁以确保没有其他broker同时访问这个数据库。多个broker同时运行并尝试去访问数据库时,只有第一个broker会连接成功并拿到锁,其他随后到来的broker会一直poll直到它可以获得锁为止。这些处于polling状态的broker,被视为slave,它们不会开启任何传输连接或者网络连接。
该配置中所有的broker可以使用同一份配置文件,这使得activemq启动起来简单得多。
shared file system master-slave
它建议使用 KahaDB 消息存储,但是对于消息存储使用底层的共享的文件系统。当KahaDB消息存储启动时,它将尝试获取文件锁,以防止任何其他broker同时访问基于文件的消息存储。
二、ActiveMQ是如何在brokers之间传递消息
ActiveMQ中有一个概念:networks of brokers,它指的是连接ActiveMQ的消息代理在一起形成不同的拓扑结构。
接下来就是分析brokers是如何在一个network中发现彼此且如何配置broker使其在network中合作。
1.存储和转发(store and forward)
ActiveMQ的存储和转发概念意味着,消息在通过network转发到其他broker之前,总是被存储在本地broker中,也就是说,如果一条消息由于连接原因没有被交付,比如说,正在重连,broker将能够通过网络连接将未交付的消息发送到远程broker。默认情况下,network仅以单向方式操作,如图:
当然,这并不是说network只能单向操作,如果想要双向操作,同样可以在远程broker中配置一个network connector指向本地的broker,或者直接指定创建的network connector为双向duplex。
当本地broker和远程broker之间建立好一条network后,远程broker会将其所有持久和处于活动的消费者的目的地信息传递给本地broker,本地broker使用这些信息去判断远程broker对哪种消息感兴趣,并转发该类型消息给它。
这里,书中举了一个场景,假如我们有多个超市需要连接到一个后台办公订购系统,这将很难灵活扩展新的超市,后台办公订购系统不好掌控所有新加入的超市即远程broker。
给个图示,这里想象超市和后台之间有一个防火墙。(至于为什么这么想象,我并不得知)注意到这里,超市broker和back office之间的network是双向的,超市broker的配置:
<networkConnectors>
<networkConnector uri="static://(tcp://backoffice:61617)"
name="bridge"
duplex="true"
conduitSubscriptions="true"
decreaseNetworkConsumerPriority="false">
</networkConnector>
</networkConnectors>
这里关于配置,主要注意一点是,配置的顺序是很重要的,关于networks,persistence,transports的顺序如下:
Networks——必须在消息存储之前创建
Message store——必须在传输配置好之前配置完
Transports——必须在broker配置的最后
举个例子:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://activemq.apache.org/schema/core">
<broker brokerName="receiver" persistent="true" useJmx="true">
<networkConnectors>
<networkConnector uri="static:(tcp://backoffice:61617)"/>
</networkConnectors>
<persistenceAdapter>
<kahaDB directory = "activemq-data"/>
</persistenceAdapter>
<transportConnectors>
<transportConnector uri="tcp://localhost:62002"/>
</transportConnectors>
</broker>
</beans>
来一张,在大型开发场景下的高可用性和network配置结合:
2.Network发现机制
ActiveMQ提供两种发现机制:
Dynamic——使用组播和会合方式搜索brokers
Static——通过一个URI列表配置brokers
使用组播发现的方式去创建network连接是最简单粗暴的,当你启动一个broker时,它会通过组播IP去搜索其他的broker并创建network连接。配置方式如下:
<networkConnectors>
<networkConnector uri="multicast://default"/>
</networkConnectors>
这里“default”表示这个broker属于哪个组,建议使用唯一的名字去标识,以免你的broker连接到其他你不知道的应用代理上。
组播发现机制有一些限制,比如说不能控制哪些broker被发现,事实上,它通常局限于本地网段上去发现其他broker,因为组播IP不通过路由器延伸。
关于第二种方式,static,事实上,在这之前的配置一直都是static,只不过broker的URL列表有点少而已,
<networkConnectors>
<networkConnector uri="static:(tcp://remote-master:61617,tcp://remote-slave:61617)"/>
</networkConnectors>
static的配置属性:
3.Network配置
对于远程broker现存在的目的地,可能没有任何活动持久的订阅者或消费者,因此,当network初始化连接到远程broker时,远程broker会读取它现存目的地的消息,并传递给本地broker,然后,本地broker也可以转发那些目的地的消息。
重要的是要注意,一个network将使用broker的名称来代表远程broker创建唯一的持久预订代理。 因此,如果在稍后的时间点更改broker的名称,很可能会通过network丢失持久主题订阅者的消息。 为避免这种情况,请确保为元素上的brokerName属性使用唯一的名称。 有关简要示例,请参阅以下内容:
<broker xmlns="http://activemq.apache.org/schema/core/"
brokerName="brokerA"
dataDirectory="${activemq.base}/data">
...
<networkConnectors>
<networkConnector name="brokerA to brokerB" uri="tcp://remotehost:61616"/>
</networkConnectors>
</broker>
关于Network配置还有很多,不一一列举了。
三、为大量并发应用程序部署ActiveMQ
扩展使用ActiveMQ的应用程序可能需要一些时间,需要一些努力。 在本节中,将介绍三种技术来帮助完成此任务。首先是垂直扩展,单个broker用于数千个连接和队列。然后将通过使用network水平扩展应用程序来扩展到数万个连接。 最后,将研究流量分区,这将平衡扩展和性能,但会增加ActiveMQ应用程序的复杂性。
1.垂直扩展
垂直扩展是一种用于增加单个ActiveMQ broker可以处理的连接数(因此增加负载)的技术。默认情况下,ActiveMQ broker设计为尽可能高效地移动消息,以确保低延迟和良好的性能。但是我们可以做一些配置调整,以确保ActiveMQ broker可以处理大量的并发连接和大量的队列。
默认情况下,ActiveMQ将使用阻塞I/O来处理传输连接。 这导致每个连接使用一个线程。 我们可以在ActiveMQ broker上使用非阻塞I/O(而客户端上仍然使用默认传输)来减少使用的线程数。broker的非阻塞I/O配置如下:
<broker>
<transportConnectors>
<transportConnector name="nio" uri="nio://localhost:61616"/>
</transportConnectors>
</broker>
除了每个连接使用一个线程来阻塞I/O外,ActiveMQ broker可以使用线程为每个客户端连接分派消息。可以通过将名为org.apache.activemq.UseDedicatedTaskRunner的系统属性设置为false,让ActiveMQ使用线程池。
ACTIVEMQ_OPTS="-Dorg.apache.activemq.UseDedicatedTaskRunner=false"
1
确保ActiveMQ broker具有足够的内存来处理大量并发连接有两步过程。
首先,需要确保启动ActiveMQ broker的JVM配置了足够的内存。
ACTIVEMQ_OPTS="-Xmx1024M -Dorg.apache.activemq.UseDedicatedTaskRunner=false"
1
第二,确保专门为ActiveMQ broker在JVM配置适当的内存量。此调整通过< system-Usage >元素的limit属性进行。(最好从512MB开始,如果测试不够再往上加),配置示例:
<systemUsage>
<systemUsage>
<memoryUsage>
<memoryUsage limit="512 mb"/>
</memoryUsage>
<storeUsage>
<storeUsage limit="10 gb" name="foo"/>
</storeUsage>
<tempUsage>
<tempUsage limit="1 gb"/>
</tempUsage>
</systemUsage>
</systemUsage>
还应该降低每一个连接的CPU负载,如果使用的OpenWire连接方式,禁用紧密编码,否则会使得CPU过度紧张。
String uri = "failover://(tcp://localhost:61616?" + " wireFormat.tightEncodingEnabled=false)";
ConnectionFactory cf = new ActiveMQConnectionFactory(uri);
前面研究的是broker怎么调整去处理数千个连接,下面开始研究的是怎么调整broker去处理数千个队列。
默认队列配置使用单独的线程来将消息从消息存储区分页到队列中,以便分发给感兴趣的消息消费者。 对于大量队列,建议通过为所有队列启用optimize-Dispatch属性来禁用此功能,
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue=">" optimizedDispatch="true"/>
</policyEntries>
</policyMap>
</destinationPolicy>
为了确保不仅可以扩展到数千个连接,而且还可以扩展到数万个队列,使用JDBC消息存储库或更新和更快的KahaDB消息存储库。 KahaDB默认情况下在ActiveMQ中启用。
到目前为止,我们已经考虑了扩展连接,减少线程使用,并选择正确的消息存储。 调整用于扩展的ActiveMQ的示例配置如以下:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="amq-broker" dataDirectory="${activemq.base}/data">
<persistenceAdapter>
<kahaDB directory="${activemq.base}/data" journalMaxFileLength="32mb"/>
</persistenceAdapter>
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue=">" optimizedDispatch="true"/>
</policyEntries>
</policyMap>
</destinationPolicy>
<systemUsage>
<systemUsage>
<memoryUsage>
<memoryUsage limit="512 mb"/>
</memoryUsage>
<storeUsage>
<storeUsage limit="10 gb" name="foo"/>
</storeUsage>
<tempUsage>
<tempUsage limit="1 gb"/>
</tempUsage>
</systemUsage>
</systemUsage>
<transportConnectors>
<transportConnector name="openwire" uri="nio://localhost:61616"/>
</transportConnectors>
</broker>
2.水平扩展
除了扩展单个broker之外,还可以使用networks来增加可用于应用程序的ActiveMQ broker的数量。 由于networks会自动将消息传递给具有感兴趣的消费者的连接broker,因此可以将客户端配置为连接到一个broker集群,随机选择一个来连接。
failover://(tcp://broker1:61616,tcp://broker2:61616)?randomize=true
为了确保队列或持久主题订阅者的消息不会在broker上孤立,需要将network配置为使用dynamicOnly和低网络prefetchSize。
<networkConnector uri="static://(tcp://remotehost:61617)"
name="bridge"
dynamicOnly="true"
prefetchSize="1">
</networkConnector>
使用network进行水平扩展会带来更多的延迟,因为潜在的消息必须在分发给消费者之前通过多个broker。
另一种替代部署提供了巨大的可扩展性和性能,但需要更多的应用规划。 这种混合解决方案称为流量分区。
3.流量分区
客户端流量分割是垂直和水平分割的混合。 通常不使用network,因为客户端应用程序决定什么流量应该到哪个broker上。 客户端应用程序必须维护多个JMS连接,并决定哪些JMS连接应用于哪些目标。
不直接使用network connection的优点是,减少在brokers之间转发消息的开销。 需要平衡这与导致典型应用程序的额外复杂性。Fig10.8是流量分区的一个使用代表: