activemq主从和集群结合的部署方式

来源:互联网 发布:学java最好的学 编辑:程序博客网 时间:2024/06/01 10:41

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

1、Master-Slave部署方式
1)shared filesystem Master-Slave部署方式

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

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

p2.png

2)shared database Master-Slave方式

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

3)Replicated LevelDB Store方式

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

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

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

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

p3.png

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-ABroker-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-ABroker-B

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

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

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

         p4.png

1、部署的配置修改

         这里以Broker-A + Broker-B建立clusterBroker-C作为Broker-Bslave为例:

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



4.ActiveMQ之Master-Slaver+负载均衡


什么叫中间件?
中间件为软件应用提供了操作系统所提供的服务之外的服务,可以把中间件描述为"软件胶水"。中间件不是操作系统的一部分,不是数据库操作系统,也不是应用软件的一部分,而是能够让软件开发者方便的处理通信、输入和输出,能够专注自己应用的部分。

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

一般中间件都提供了横向扩展和纵向扩展,横向扩展就是我们经常说的负载均衡,纵向扩展提供了Master-Slaver;

负载均衡:提供负载均衡的中间件都对外提供服务
Master-Slaver:同时只有一个中间件对外提供服务,当Master出现挂机等问题,Slaver会自动接管

看一个整合的简图:

Master-Slave和Broker Cluster

准备:
jdk1.6,apache-activemq-5.10.0,mysql5.1,zookeeper-3.4.3

先来看看Master-Slave模式

Shared File System Master Slave

本次测试在同一台机器上:
首先更改配置conf/activemq,做如下修改:

[java] view plain copy
  1. <persistenceAdapter>  
  2.         <!--<kahaDB directory="${activemq.data}/kahadb"/>-->  
  3.     <kahaDB directory="D:/kahaDB"/>  
  4.  </persistenceAdapter>  
将activemq拷贝3份,分别:apache-activemq-5.10.0-M1, apache-activemq-5.10.0-M2,apache-activemq-5.10.0-M3,分别启动activemq命令,启动的日志分别是: 

表示当前进程是Master


表示当前进程没有获取到锁,作为Slaver

测试:

下面的例子中分别提供了Producer(Sender类)Consumer(Receiver类)
我们首先用Producer发送消息给activemq,然后停止Master,然后再用Consumer接受消息,测试结果是可以接受到数据的。

2).JDBC Master Slave
JDBC Master Slave模式和Shared File Sysytem Master Slave模式的原理是一样的,只是把共享文件系统换成了共享数据库。

修改配置文件conf/activemq

[java] view plain copy
  1. <pre code_snippet_id="1660918" snippet_file_name="blog_20160425_10_9093290" name="code" class="html"><persistenceAdapter>  
  2.         <!--<kahaDB directory="${activemq.data}/kahadb"/>-->  
  3.     <!--<kahaDB directory="D:/kahaDB"/>-->  
  4.         <jdbcPersistenceAdapter dataDirectory="${activemq.base}/data" dataSource="#mysql-ds"/>   
  5.   </persistenceAdapter></pre><br><br>  
添加数据源:
[java] view plain copy
  1. <pre code_snippet_id="1660918" snippet_file_name="blog_20160425_11_1808506" name="code" class="html"><bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  2.     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>  
  3.     <property name="url" value="jdbc:mysql://localhost/activemq?relaxAutoCommit=true"/>  
  4.     <property name="username" value="root"/>  
  5.     <property name="password" value="root"/>  
  6.     <property name="poolPreparedStatements" value="true"/>  
  7.     </bean></pre><br><br>  
注:这里使用的是mysql,所以需要mysql驱动程序: mysql-connector-java-5.1.18,讲jar包放入lib文件夹下面,驱动版本不对,会出现如下错误: Database lock driver override not found for : [mysql_connect ...


分别拷贝到其他几个文件夹下,分别启动,启动成功后我们可以看到数据库中多了几张表:


测试方式同上;
官网手册:http://activemq.apache.org/jdbc-master-slave.html

3).Replicated LevelDB Store
这种方式是ActiveMQ5.9以后才新增的特性,使用ZooKeeper协调选择一个node作为master

修改配置文件conf/activemq:

[java] view plain copy
  1. <pre code_snippet_id="1660918" snippet_file_name="blog_20160425_12_5047109" name="code" class="html"><!--<persistenceAdapter>  
  2.          <kahaDB directory="${activemq.data}/kahadb"/>  
  3.          <kahaDB directory="D:/kahaDB"/>  
  4.          <jdbcPersistenceAdapter dataDirectory="${activemq.base}/data" dataSource="#mysql-ds"/>   
  5.  </persistenceAdapter>-->  
  6.  <persistenceAdapter>  
  7.     <replicatedLevelDB  
  8.         directory="${activemq.data}/leveldb"  
  9.         replicas="3"  
  10.         bind="tcp://0.0.0.0:0"  
  11.         zkAddress="127.0.0.1:2181"  
  12.         hostname="127.0.0.1"  
  13.         sync="local_disk"  
  14.         zkPath="/activemq/leveldb-stores"/>  
  15.  </persistenceAdapter></pre><br><br>  
首先启动zookeeper,这里没有做集群处理,默认端口是2181,然后分别启动activemq, 
启动之后报错:"activemq LevelDB IOException handler"。 

原因:版本5.10.0存在的依赖冲突。

解决方案:
(1)移除lib目录中的pax-url-aether-1.5.2.jar包
(2)注释掉配置文件中的日志配置; 

[java] view plain copy
  1. <!-- Allows accessing the server log  
  2.     <bean id="logQuery" class="org.fusesource.insight.log.log4j.Log4jLogQuery"  
  3.           lazy-init="false" scope="singleton"  
  4.           init-method="start" destroy-method="stop">  
  5.     </bean>  
  6. -->  

测试方式同上;

提供Java版的例子:

[java] view plain copy
  1. <pre code_snippet_id="1660918" snippet_file_name="blog_20160425_13_2397151" name="code" class="java">public class Sender {  
  2.     private static final int SEND_NUMBER = 5;  
  3.   
  4.     public static void main(String[] args) {  
  5.         ConnectionFactory connectionFactory;  
  6.         Connection connection = null;  
  7.         Session session;  
  8.         Destination destination;  
  9.         MessageProducer producer;  
  10.         connectionFactory = new ActiveMQConnectionFactory(  
  11.             ActiveMQConnection.DEFAULT_USER,  
  12.             ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616");  
  13.         try {  
  14.             connection = connectionFactory.createConnection();  
  15.             connection.start();  
  16.             session = connection.createSession(Boolean.TRUE,  
  17.                     Session.AUTO_ACKNOWLEDGE);  
  18.             destination = session.createQueue("FirstQueue");  
  19.             producer = session.createProducer(destination);  
  20.             producer.setDeliveryMode(DeliveryMode.PERSISTENT);  
  21.             sendMessage(session, producer);  
  22.             session.commit();  
  23.         } catch (Exception e) {  
  24.             e.printStackTrace();  
  25.         } finally {  
  26.             try {  
  27.                 if (null != connection)  
  28.                     connection.close();  
  29.             } catch (Throwable ignore) {  
  30.             }  
  31.         }  
  32.     }  
  33.   
  34.     public static void sendMessage(Session session, MessageProducer producer)  
  35.             throws Exception {  
  36.         for (int i = 1; i <= SEND_NUMBER; i++) {  
  37.             TextMessage message = session.createTextMessage("发送的消息"  
  38.                     + i);  
  39.             System.out.println("发送消息:" + "ActiveMq 发送的消息" + i);  
  40.             producer.send(message);  
  41.         }  
  42.     }  
  43. }  
  44. public class Receiver {  
  45.     public static void main(String[] args) {  
  46.         ConnectionFactory connectionFactory;  
  47.         Connection connection = null;  
  48.         Session session;  
  49.         Destination destination;  
  50.         MessageConsumer consumer;  
  51.         connectionFactory = new ActiveMQConnectionFactory(  
  52.             ActiveMQConnection.DEFAULT_USER,  
  53.             ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616");  
  54.         try {  
  55.             connection = connectionFactory.createConnection();  
  56.             connection.start();  
  57.             session = connection.createSession(Boolean.FALSE,  
  58.                     Session.AUTO_ACKNOWLEDGE);  
  59.             destination = session.createQueue("FirstQueue");  
  60.             consumer = session.createConsumer(destination);  
  61.             while (true) {  
  62.                 TextMessage message = (TextMessage) consumer.receive(100000);  
  63.                 if (null != message) {  
  64.                     System.out.println("收到消息" + message.getText());  
  65.                 } else {  
  66.                     break;  
  67.                 }  
  68.             }  
  69.         } catch (Exception e) {  
  70.             e.printStackTrace();  
  71.         } finally {  
  72.             try {  
  73.                 if (null != connection)  
  74.                     connection.close();  
  75.             } catch (Throwable ignore) {  
  76.             }  
  77.         }  
  78.     }  
  79. }</pre><br><br>  


5.Broker-Cluster实现负载均衡

Broker-Cluster部署方式中,各个broker通过网络互相连接,并共享queue,提供了2中部署方式:
static Broker-ClusterDynamic Broker-Cluster

1).static Broker-Cluster

将ActiveMq拷贝2份,分别命名:apache-activemq-5.10.0_M1,apache-activemq-5.10.0_M2,
下面就是配置activemq,xml:
M1做如下配置:

?
1
2
3
<networkConnectors>
    <networkConnector uri="static:(tcp://localhost:61617)"/>
</networkConnectors>
?
1
2
3
<transportConnectors>
        <transportConnector name="openwire"uri="tcp://0.0.0.0:61616?maximumConnectio        ns=1000&amp;wireFormat.maxFrameSize=104857600"/>
 </transportConnectors>
M2做如下配置: 
?
1
2
3
<networkConnectors>
    <networkConnector uri="static:(tcp://localhost:61616)"/>
</networkConnectors>
?
1
2
3
<transportConnectors>
        <transportConnector name="openwire"uri="tcp://0.0.0.0:61617?maximumConnectio        ns=1000&amp;wireFormat.maxFrameSize=104857600"/>
 </transportConnectors>
通过以上配置使M1和M2这两个 broker通过网络互相连接,并共享queue,
启动M1和M2,可以看到如下启动日志:
 


可以看到M1和M2,network connection has been established

测试我们还是用上一篇中的Sender和Receiver类,只需要做一点点修改:
Sender类还是链接tcp://localhost:61616,发送消息到queue,
Receiver做如下修改:

?
1
2
3
connectionFactory = newActiveMQConnectionFactory(
    ActiveMQConnection.DEFAULT_USER,
    ActiveMQConnection.DEFAULT_PASSWORD,"tcp://localhost:61617");

经测试Receiver可以接受到数据,表示M1和M2已经共享了queue

2).Dynamic Broker-Cluster

Dynamic Discovery集群方式在配置ActiveMQ实例时,不需要知道所有其它实例的URI地址
对activemq.xml做如下配置:
M1做如下配置:

?
1
2
3
<networkConnectors>
    <networkConnector uri="multicast://default"/>
</networkConnectors>
?
1
2
3
4
<transportConnectors>
        <transportConnector name="openwire"uri="tcp://0.0.0.0:61616? maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"
          discoveryUri="multicast://default"/>
 </transportConnectors>
M2做如下配置:
?
1
2
3
<networkConnectors>
    <networkConnector uri="multicast://default"/>
</networkConnectors>
?
1
2
3
4
<transportConnectors>
        <transportConnector name="openwire"uri="tcp://0.0.0.0:61617? maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"
          discoveryUri="multicast://default"/>
 </transportConnectors>
启动M1和M2,可以看到如下启动日志: network connection has been established

测试同static broker-cluster,可以得到相同的结果。
官网配置说明:http://activemq.apache.org/networks-of-brokers.html

Master-Slaver保证了数据的可靠性,Broker-Cluster提供了负载均衡,所以一般正式环境中都会采用:
Master-Slaver+Broker-Cluster的模式

0 0
原创粉丝点击