ActiveMQ的集群与高可用

原文:
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="&gt;" 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是流量分区的一个使用代表:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容