为什么要对消息中间件集群?

实现高可用,以排除单点故障引起的服务中断

实现负载均衡,以提升效率为更多用户提供服务

 

集群方式:

客户端集群:让多个消费者消费同一个队列

Broker clusters:多个Broker之间同步消息

Master Slave:实现高可用

 

客户端配置:

ActiveMQ失效转移(falilover):

允许当其中一台消息服务器宕机时,客户端在传输层上重新连接到其他消息服务器。

语法:failover:(uri1,....,uriN)?transportOptions

transportOptions参数说明:

randomize:默认为true,表示在URI列表中选择URI连接时是否采用随机策略

initialReconnectDelay:默认10毫秒,表示第一次尝试重连之间等待的时间

maxReconnectDelay:默认30000毫秒,表示最长重连的时间间隔

 

Broker clusters集群配置:

原理:两个节点,节点A和节点B,节点A可以把消息同步到节点B,节点B可以把消息同步到节点A,同步之后,节点A接收到的消息可以被节点B的消费者消费,反之亦然,其实现方式是通过"网络连接器"来实现的。

网络连接器:网络连接器主要用于配置ActiveMQ服务器与服务器之间的网络通讯方式,用于服务器透传消息。又分为静态连接器和动态连接器。

静态连接器配置:

<networkConnectors>

<networkConnector uri="static:(tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)"/>

</networkConnectors>

动态连接器配置:

 

<networkConnectors>

<networkConnector uri="multicast://default"/>

</networkConnectors>

<transportConnectors>

<transportConnector uri="tcp://localhost:0" discoveryUri="multicast://default"/>

</transportConnectors>

 

 

Master Slave集群配置:

ActiveMQ Master Slave集群方案:

1.Shared storage master/slave 共享存储

2.Replicated LevelDB Store 基于复制的LevelDB Store 

 

共享存储集群的原理:

比如有两台服务器节点A和节点B,他们有一个共享的持久化地址,先启动节点A,A获得排它锁独占资源成为Master,B成为Slave,若A挂掉,B将获得持久化资源的排它锁成为Master接收客户端的请求,客户端通过失效转移将请求发送到了B,完成了请求的不间断性,达到高可用效果。

 

基于复制的LevelDB Store的原理:

 因为LevelDB是基于ZK的,所以它的服务器至少有三个,每个节点都有自己的储存化方式,他们共有一个zk节点,通过zk来选取一台服务器作为Master,A就具有了提供给外部服务的能力,获得外部消息资源后在本地"store"储存,再通过zk将消息同步给B和C储存起来,若A故障,zk会选取另一台服务器成为Master。

activemq配置文件在哪 docker activemq配置参数_负载均衡

 

 

 

两种集群方式的对比

activemq配置文件在哪 docker activemq配置参数_集群_02

 

Master/Slave实现了高可用,因为一台服务器挂掉,另一台服务器可以马上顶替上去,且保证了消息不会丢失,但是无法做到负载均衡,因为Slave服务器不能提供服务。

Broker Cluster不能实现高可用,因为它获得的消息没有做持久化,若某个服务器挂掉,它正在处理的消息就会丢失,但是做到了负载均衡,因为A节点的消息可以被节点B的消费者消费,B节点的消息可以被节点A的消费者消费。

 

 

如何做到两者兼备呢?

下面是网上找的一种三台服务器的完美集群方案:

activemq配置文件在哪 docker activemq配置参数_消息中间件_03

无论A,B,C哪个服务器宕机,都不会影响服务的正常运行,保证了高可用,又因为B、C和A之间的消息同步,实现了B、A或C、A之间可以互相消费彼此的消息,实现了负载均衡。

 

ActiveMQ三台服务器的完美集群具体配置方案:

因为只有一台电脑,所以只能通过开放不同端口的方法来实现一台电脑部署三个服务器

activemq配置文件在哪 docker activemq配置参数_activemq_04

先新建三个actvemq服务目录activemq-A,activemq-B,activemq-C,一个持久化目录share-DB

 

activemq配置文件在哪 docker activemq配置参数_集群_05

 

将apache-activemq-5.14.2复制进A,B,C三个目录中

进入activemq-A的apache-activemq-5.14.2的conf,编辑activemq.xml

 

activemq配置文件在哪 docker activemq配置参数_负载均衡_06

 

将除了端口61616的配置注释掉

activemq配置文件在哪 docker activemq配置参数_服务器_07

 

添加网络连接器配置,将B,C的uri添加进来:

activemq配置文件在哪 docker activemq配置参数_服务器_08

 

还需要编辑conf目录下的jetty.xml(提供后端管理地址的jetty服务器)

activemq配置文件在哪 docker activemq配置参数_负载均衡_09

因为A节点使用的就是默认的8161端口,因此不用修改

 

 

 

以下是B节点activemq.xml的配置:

这是持久化目录的配置

 

<persistenceAdapter>
            <kahaDB directory="/opt/app/actvemq/share-DB"/>
        </persistenceAdapter>

 

 

网络连接器的配置:

 

<transportConnector name="openwire" uri="tcp://0.0.0.0:61617?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <!--<transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="mqtt" uri="mqtt://0.0.0.0:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="ws" uri="ws://0.0.0.0:61614?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>-->
        </transportConnectors>
        <networkConnectors>
                <networkConnector name="network_A" uri="static:(tcp://127.0.0.1:61616)"/>
        </networkConnectors>

 

 

jetty服务端口则改为8162

 

 

C节点配置就不一一展示了,模仿B节点修改就行。

 

最后依次启动A,B,C节点,查看相应端口,可以看出因为先启动activemq-B所以它成为Master,C节点成为Slave不提供服务(进程状态TiIME_WAIT)

activemq配置文件在哪 docker activemq配置参数_消息中间件_10

 

 

用我的博客"简单使用ActiveMQ"里的代码进行测试,不过还要进行客户端失效转移的配置,很简单就修改两处地方:

AppPoducer.java:

 

private static final String url="failover:(tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?randomize=true";

AppConsumer.java:

 

 

private static final String url="failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?randomize=true";