ActiveMQ的集群与高可用

1、ActiveMQ的高可用性
ActiveMQ使用master-slave模式实现高可用性,提供两种实现主从模式的配置:shared nothing、shared storage(a relational database and a shared file system)java

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挂掉。apache

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。dom

配置shared nothing master-slave
配置一个broker成为slave很简单,配置一个masterConnector service:tcp

<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同时访问基于文件的消息存储。

2、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配置还有不少,不一一列举了。

3、为大量并发应用程序部署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是流量分区的一个使用表明.

欢迎学Java和大数据的朋友们加入java架构交流: 855835163

加群连接:https://jq.qq.com/?_wv=1027&amp;k=5dPqXGI

群内提供免费的架构资料还有:Java工程化、高性能及分布式、高性能、深刻浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的免费直播讲解  能够进来一块儿学习交流哦