ActiveMQ集群部署详解


一、ActiveMQ简介

1、什么是ActiveMQ

ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。

2、ActiveMQ特性

⒈ 多种语言和协议编写客户端。语言: Java,C,C++,C#,Ruby,Perl,Python,PHP。应用协议: OpenWire,Stomp REST,WS Notification,XMPP,AMQP

⒉ 完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务)。

⒊ 对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring2.0的特性。

⒋ 通过了常见J2EE服务器(如 Geronimo,JBoss 4,GlassFish,WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ可以自动的部署到任何兼容J2EE 1.4 商业服务器上。

⒌ 支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA。

⒍ 支持通过JDBC和journal提供高速的消息持久化。

⒎ 从设计上保证了高性能的集群,客户端-服务器,点对点。

⒏ 支持Ajax

扫描二维码关注公众号,回复: 3874094 查看本文章

⒐ 支持与Axis的整合。

⒑ 可以很容易的调用内嵌JMS provider,进行测试。

二、ActiveMQ单点部署

1、下载

到官网http://activemq.apache.org/下载对应的版本,目前官网最新的版本为5.15.1,这里我们下载apache-activemq-5.13.4-bin.tar这个版本做讲解。

2、解压到指定目录

我们将apache-activemq-5.13.4-bin.tar压缩包解压到/usr/local目录下,解压命令:

tar zxvf /apache-activemq-5.13.4-bin.tar -C /usr/local


3、修改端口(可不改)

ActiveMQ的服务端口默认为61616,位于activemq.xml配置文件。

ActiveMQ的控制台端口默认为8161,位于jetty.xml配置文件。

<transportConnectors>

<transportConnector name="openwire" uri="tcp://localhost:61616"discoveryUri="multicast://default"/>

</transportConnectors>


<bean id="jettyPort"class="org.apache.activemq.web.WebConsolePort"init-method="start">

<property name="port" value="8161"/>

</bean>


4、启动MQ

/usr/local/apache-activemq-5.13.4/bin/activemq start


5、测试安装是否成功

我们通过客户端的测试方法测试我们安装的MQ是否正常可用


发送端代码:

import javax.jms.Connection;

import javax.jms.ConnectionFactory;

import javax.jms.DeliveryMode;

import javax.jms.Destination;

import javax.jms.MessageProducer;

import javax.jms.Session;

import javax.jms.TextMessage;


import org.apache.activemq.ActiveMQConnection;

import org.apache.activemq.ActiveMQConnectionFactory;


/**

* MQ发送消息测试

*

* @author Administrator

*

*/

public class Sender {


private static final int SEND_NUMBER = 5;


public static void main(String[] args) {

// ConnectionFactory:连接工厂,JMS用它创建连接

ConnectionFactory connectionFactory;

// Connection:JMS客户端到JMS Provider的连接

Connection connection = null;

// Session:一个发送或接收消息的线程

Session session;

// Destination:消息的目的地;消息发送给谁.

Destination destination;

// MessageProducer:消息发送者

MessageProducer producer;

// TextMessage message;

// 构造ConnectionFactory实例对象,此处采用ActiveMq的实现jar

connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "failover:(tcp://localhost:61616)");

try {

// 构造从工厂得到连接对象

connection = connectionFactory.createConnection();

// 启动

connection.start();

// 获取操作连接

session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

// 获取session

destination = session.createQueue("FirstQueue");

// 得到消息生成者【发送者】

producer = session.createProducer(destination);

// 设置不持久化,此处学习,实际根据项目决定

producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

// 构造消息,此处写死,项目就是参数,或者方法获取

sendMessage(session, producer);

session.commit();

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (null != connection) {

connection.close();

}

} catch (Throwable ignore) {

}

}

}


public static void sendMessage(Session session, MessageProducer producer) throws Exception {

for (int i = 1; i <= SEND_NUMBER; i++) {

TextMessage message = session.createTextMessage("ActiveMq发送的消息" + i);

// 发送消息到目的地方

System.out.println("发送消息:" + "ActiveMq 发送的消息" + i);

producer.send(message);

}

}


}


接收端代码:

import javax.jms.Connection;

import javax.jms.ConnectionFactory;

import javax.jms.Destination;

import javax.jms.MessageConsumer;

import javax.jms.Session;

import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;

import org.apache.activemq.ActiveMQConnectionFactory;


public class Receive {


public static void main(String[] args) {

// ConnectionFactory:连接工厂,JMS用它创建连接

ConnectionFactory connectionFactory;

// Connection:JMS客户端到JMS Provider的连接

Connection connection = null;

// Session:一个发送或接收消息的线程

Session session;

// Destination:消息的目的地;消息发送给谁.

Destination destination;

// 消费者,消息接收者

MessageConsumer consumer;

connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "failover:(tcp://localhost:61616)");

try {

// 构造从工厂得到连接对象

connection = connectionFactory.createConnection();

// 启动

connection.start();

// 获取操作连接

session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

// 获取session

destination = session.createQueue("FirstQueue");

consumer = session.createConsumer(destination);

while (true) {

// 设置接收者接收消息的时间,为了便于测试,这里谁定为100s

TextMessage message = (TextMessage) consumer.receive(100000);

if (null != message) {

System.out.println("收到消息" + message.getText());

} else {

break;

}

}


} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (null != connection) {

connection.close();

}

} catch (Throwable ignore) {

}

}

}

}


首先运行发送端main方法,会打印出日志:

发送消息:ActiveMq 发送的消息1

发送消息:ActiveMq 发送的消息2

发送消息:ActiveMq 发送的消息3

发送消息:ActiveMq 发送的消息4

发送消息:ActiveMq 发送的消息5


接着我们访问控制台地址:

http://localhost:8161/admin/queues.jsp

登录账号密码为admin/admin,可对jetty-realm.properties进行修改,配置文件位于/usr/local/apache-activemq-5.13.4/conf文件夹。


我们可以看到里边队列里有5条数据。



执行接收端main方法,则会打印:

收到消息ActiveMq发送的消息1

收到消息ActiveMq发送的消息2

收到消息ActiveMq发送的消息3

收到消息ActiveMq发送的消息4

收到消息ActiveMq发送的消息5


以上结果说明ActiveMQ安装成功。


三、ActiveMQ分布式集群部署


单点的ActiveMQ部署虽然最简单,但无法满足企业级应用的高可用性和集群的需求,因此ActiveMQ提供了master-slave、broker cluster等多种部署方式,但通过分析多种部署方式之后我认为需要将两种部署方式相结合才能满足我们公司分布式和高可用的需求,所以后面就重点将解如何将两种部署方式相结合。

1、Master-Slave部署方式

1)shared filesystem Master-Slave部署方式

主要是通过共享存储目录来实现master和slave的热备,所有的ActiveMQ应用都在不断地获取共享目录的控制权,哪个应用抢到了控制权,它就成为master。

多个共享存储目录的应用,谁先启动,谁就可以最早取得共享目录的控制权成为master,其他的应用就只能作为slave。



2)shared database Master-Slave方式

与shared filesystem方式类似,只是共享的存储介质由文件系统改成了数据库而已。

3)Replicated LevelDB Store方式

这种主备方式是ActiveMQ5.9以后才新增的特性,使用ZooKeeper协调选择一个node作为master。被选择的master broker node开启并接受客户端连接。

其他node转入slave模式,连接master并同步他们的存储状态。slave不接受客户端连接。所有的存储操作都将被复制到连接至Master的slaves。

如果master死了,得到了最新更新的slave被允许成为master。fialed node能够重新加入到网络中并连接master进入slave mode。所有需要同步的disk的消息操作都将等待存储状态被复制到其他法定节点的操作完成才能完成。所以,如果你配置了replicas=3,那么法定大小是(3/2)+1=2. Master将会存储并更新然后等待 (2-1)=1个slave存储和更新完成,才汇报success。至于为什么是2-1,熟悉Zookeeper的应该知道,有一个node要作为观擦者存在。

单一个新的master被选中,你需要至少保障一个法定node在线以能够找到拥有最新状态的node。这个node将会成为新的master。因此,推荐运行至少3个replica nodes,以防止一个node失败了,服务中断。


配置也简单,首先选择3台服务器,修改配置文件activemq.xml(位于/usr/local/apache-activemq-5.13.4/conf),将每个配置文件的brokerName设置成相同的名称,接着配置replicatedLevelDB的zkAddress和hostname即可,配置文件如下


<!--

   Licensed to the Apache Software Foundation (ASF) under one or more

   contributor license agreements.  See the NOTICE file distributed with

   this work for additional information regarding copyright ownership.

   The ASF licenses this file to You under the Apache License, Version 2.0

   (the "License"); you may not use this file except in compliance with

   the License.  You may obtain a copy of the License at


   http://www.apache.org/licenses/LICENSE-2.0


   Unless required by applicable law or agreed to in writing, software

   distributed under the License is distributed on an "AS IS" BASIS,

   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

   See the License for the specific language governing permissions and

   limitations under the License.

-->

<!-- START SNIPPET: example -->

<beans

 xmlns="http://www.springframework.org/schema/beans"

 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

 http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">


   <!-- Allows us to use system properties as variables in this configuration file -->

   <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

       <property name="locations">

           <value>file:${activemq.conf}/credentials.properties</value>

       </property>

   </bean>


  <!-- Allows accessing the server log -->

   <bean id="logQuery" class="io.fabric8.insight.log.log4j.Log4jLogQuery"

         lazy-init="false" scope="singleton"

         init-method="start" destroy-method="stop">

   </bean>


   <!--

       The <broker> element is used to configure the ActiveMQ broker.

   -->

   <broker xmlns="http://activemq.apache.org/schema/core" brokerName="myBrokerName" dataDirectory="${activemq.data}">


       <destinationPolicy>

           <policyMap>

             <policyEntries>

               <policyEntry topic=">" >

                   <!-- The constantPendingMessageLimitStrategy is used to prevent

                        slow topic consumers to block producers and affect other consumers

                        by limiting the number of messages that are retained

                        For more information, see:


                        http://activemq.apache.org/slow-consumer-handling.html


                   -->

                 <pendingMessageLimitStrategy>

                   <constantPendingMessageLimitStrategy limit="1000"/>

                 </pendingMessageLimitStrategy>

               </policyEntry>

             </policyEntries>

           </policyMap>

       </destinationPolicy>



       <!--

           The managementContext is used to configure how ActiveMQ is exposed in

           JMX. By default, ActiveMQ uses the MBean server that is started by

           the JVM. For more information, see:


           http://activemq.apache.org/jmx.html

       -->

       <managementContext>

           <managementContext createConnector="false"/>

       </managementContext>


       <!--

           Configure message persistence for the broker. The default persistence

           mechanism is the KahaDB store (identified by the kahaDB tag).

           For more information, see:


           http://activemq.apache.org/persistence.html

       -->

<!--

       <persistenceAdapter>

           <kahaDB directory="${activemq.data}/kahadb"/>

       </persistenceAdapter>

-->

<persistenceAdapter>  

        <replicatedLevelDB   

             directory="${activemq.data}/leveldb"  

             replicas="3"  

             bind="tcp://0.0.0.0:0"  

             zkAddress="10.27.167.118:2181,10.27.167.66:2181"  

             hostname="10.27.167.118"  

             sync="local_disk"  

             zkPath="/activemq/leveldb-stores"/>  

       </persistenceAdapter>



         <!--

           The systemUsage controls the maximum amount of space the broker will

           use before disabling caching and/or slowing down producers. For more information, see:

           http://activemq.apache.org/producer-flow-control.html

         -->

         <systemUsage>

           <systemUsage>

               <memoryUsage>

                   <memoryUsage percentOfJvmHeap="70" />

               </memoryUsage>

               <storeUsage>

                   <storeUsage limit="100 gb"/>

               </storeUsage>

               <tempUsage>

                   <tempUsage limit="50 gb"/>

               </tempUsage>

           </systemUsage>

       </systemUsage>


       <!--

           The transport connectors expose ActiveMQ over a given protocol to

           clients and other brokers. For more information, see:


           http://activemq.apache.org/configuring-transports.html

       -->

       <transportConnectors>

           <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB -->

           <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

           <transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

           <transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

           <transportConnector name="mqtt" uri="mqtt://0.0.0.0:1883?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

           <transportConnector name="ws" uri="ws://0.0.0.0:61614?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

       </transportConnectors>


       <!-- destroy the spring context on shutdown to stop jetty -->

       <shutdownHooks>

           <bean xmlns="http://www.springframework.org/schema/beans" class="org.apache.activemq.hooks.SpringContextHook" />

       </shutdownHooks>


   </broker>


   <!--

       Enable web consoles, REST and Ajax APIs and demos

       The web consoles requires by default login, you can disable this in the jetty.xml file


       Take a look at ${ACTIVEMQ_HOME}/conf/jetty.xml for more details

   -->

   <import resource="jetty.xml"/>


</beans>

<!-- END SNIPPET: example -->


2、Broker-Cluster部署方式

前面的Master-Slave的方式虽然能解决多服务热备的高可用问题,但无法解决负载均衡和分布式的问题。Broker-Cluster的部署方式就可以解决负载均衡的问题。

Broker-Cluster部署方式中,各个broker通过网络互相连接,并共享queue。当broker-A上面指定的queue-A中接收到一个message处于pending状态,而此时没有consumer连接broker-A时。如果cluster中的broker-B上面由一个consumer在消费queue-A的消息,那么broker-B会先通过内部网络获取到broker-A上面的message,并通知自己的consumer来消费。

(1)、static Broker-Cluster部署

在activemq.xml文件中静态指定Broker需要建立桥连接的其他Broker:

1、  首先在Broker-A节点中添加networkConnector节点:

<networkConnectors>

<networkConnector   uri="static:(tcp:// 0.0.0.0:61617)"duplex="false"/>

</networkConnectors>


2、  修改Broker-A节点中的服务提供端口为61616:

<transportConnectors>

<transportConnectorname="openwire"uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

</transportConnectors>


3、  在Broker-B节点中添加networkConnector节点:

<networkConnectors>

<networkConnector   uri="static:(tcp:// 0.0.0.0:61616)"duplex="false"/>

</networkConnectors>


4、  修改Broker-A节点中的服务提供端口为61617:

<transportConnectors>

<transportConnectorname="openwire"uri="tcp://0.0.0.0:61617?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

</transportConnectors>


5、分别启动Broker-A和Broker-B。

(2)、Dynamic Broker-Cluster部署

    在activemq.xml文件中不直接指定Broker需要建立桥连接的其他Broker,由activemq在启动后动态查找:

1、  首先在Broker-A节点中添加networkConnector节点:


<networkConnectors>

<networkConnectoruri="multicast://default"

          dynamicOnly="true"

          networkTTL="3"

          prefetchSize="1"

          decreaseNetworkConsumerPriority="true" />

</networkConnectors>



2、修改Broker-A节点中的服务提供端口为61616:

<transportConnectors>

        <transportConnectorname="openwire"uri="tcp://0.0.0.0:61616? " discoveryUri="multicast://default"/>

</transportConnectors>


3、在Broker-B节点中添加networkConnector节点:

<networkConnectors>

<networkConnectoruri="multicast://default"

      dynamicOnly="true"

      networkTTL="3"

      prefetchSize="1"

          decreaseNetworkConsumerPriority="true" />

</networkConnectors>


4、修改Broker-B节点中的服务提供端口为61617:

<transportConnectors>

<transportConnectorname="openwire"uri="tcp://0.0.0.0:61617" discoveryUri="multicast://default"/>

</transportConnectors>


5、启动Broker-A和Broker-B

3、Master-Slave与Broker-Cluster相结合的部署方式

可以看到Master-Slave的部署方式虽然解决了高可用的问题,但不支持负载均衡,Broker-Cluster解决了负载均衡,但当其中一个Broker突然宕掉的话,那么存在于该Broker上处于Pending状态的message将会丢失,无法达到高可用的目的。

由于目前ActiveMQ官网上并没有一个明确的将两种部署方式相结合的部署方案,但是我们可以尝试者把两者结合起来部署,如下图:


部署的配置修改,这里以Broker-A + Broker-B建立cluster,Broker-C作为Broker-B的slave为例:

1)首先在Broker-A节点中添加networkConnector节点:

<networkConnectors>

<networkConnector   uri="masterslave:(tcp://0.0.0.0:61617,tcp:// 0.0.0.0:61618)" duplex="false"/>

</networkConnectors>


2)修改Broker-A节点中的服务提供端口为61616:

<transportConnectors>

<transportConnectorname="openwire"uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

</transportConnectors>


3)在Broker-B节点中添加networkConnector节点:

<networkConnectors>

<networkConnector   uri="static:(tcp:// 0.0.0.0:61616)"duplex="false"/>

</networkConnectors>



4)修改Broker-B节点中的服务提供端口为61617:

<transportConnectors>

<transportConnectorname="openwire"uri="tcp://0.0.0.0:61617?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

</transportConnectors>


5)修改Broker-B节点中的持久化方式:

<persistenceAdapter>

      <kahaDB directory="/localhost/kahadb"/>

</persistenceAdapter>


6)在Broker-C节点中添加networkConnector节点:

<networkConnectors>

<networkConnector   uri="static:(tcp:// 0.0.0.0:61616)"duplex="false"/>

</networkConnectors>


7)修改Broker-C节点中的服务提供端口为61618:

<transportConnectors>

<transportConnectorname="openwire"uri="tcp://0.0.0.0:61618?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>

</transportConnectors>


8)修改Broker-B节点中的持久化方式:

<persistenceAdapter>

      <kahaDB directory="/localhost/kahadb"/>

</persistenceAdapter>


9)分别启动broker-A、broker-B、broker-C,因为是broker-B先启动,所以“/localhost/kahadb”目录被lock住,broker-C将一直处于挂起状态,当人为停掉broker-B之后,broker-C将获取目录“/localhost/kahadb”的控制权,重新与broker-A组成cluster提供服务。



猜你喜欢

转载自blog.csdn.net/tjcyjd/article/details/78298099