一、ActiveMQ集群的简单介绍
从 ActiveMQ 5.9 开始,ActiveMQ 的集群实现方式取消了传统的 Master-Slave 方式,增加了基于ZooKeeper + LevelDB 的 Master-Slave 实现方式。
其他两种方式目录共享和数据库共享依然存在。
三种集群方式的对比:
(1)基于共享文件系统(KahaDB,默认):
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
(2)基于JDBC:
<bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/amq?relaxAutoCommit=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="maxActive" value="20"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
<persistenceAdapter>
<jdbcPersistenceAdapter dataDirectory="${activemq.data}" dataSource="#mysql-ds" createTablesOnStartup="false"/>
</persistenceAdapter>
(3)基于可复制的 LevelDB(本教程采用这种集群方式):
LevelDB是 Google 开发的一套用于持久化数据的高性能类库。LevelDB 并不是一种服务,用户需要自行实现 Server。是单进程的服务,能够处理十亿级别规模 Key-Value 型数据,占用内存小。
<persistenceAdapter>
<replicatedLevelDB directory="${activemq.data}/leveldb" replicas="3" bind="tcp://0.0.0.0:62621" zkAddress="localhost:2181,localhost:2182,localhost:2183" hostname="localhost" zkPath="/activemq/leveldb-stores"/>
</persistenceAdapter>
本节课程主要讲解基于 ZooKeeper 和 LevelDB 搭建 ActiveMQ 集群。集群仅提供主备方式的高可用集群功能,避免单点故障,没有负载均衡功能。
官方文档:http://activemq.apache.org/replicated-leveldb-store.html
集群原理图
高可用的原理:使用 ZooKeeper(集群)注册所有的ActiveMQ Broker。只有其中的一个Broker可以提供服务,被视为 Master,其他的 Broker 处于待机状态,被视为 Slave。
如果 Master 因故障而不能提供服务,ZooKeeper 会从 Slave 中选举出一个 Broker 充当 Master。
Slave 连接 Master 并同步他们的存储状态,Slave 不接受客户端连接。所有的存储操作都将被复制到连接至 Master 的 Slaves。如果 Master 宕了,
得到了最新更新的 Slave 会成为 Master。故障节点在恢复后会重新加入到集群中并连接 Master 进入 Slave 模式。
所有需要同步的 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失败了,服务中断。
二、集群环境的搭建
1、ActiveMQ集群环境规划
- 环境:CentOS6.5、JDK1.7
- 版本:ActiveMQ 5.11.5
- zookeeper集群环境:
192.168.31.154:2181
192.168.31.117:2182
192.168.31.146:2183
主机 | 集群端口 | 消息端口 | 管控台端口 | 节点安装目录 |
---|---|---|---|---|
192.168.31.154 | 62621 | 51511 | 8161 | /home/dreyer/activemq |
192.168.31.117 | 62622 | 51512 | 8162 | /home/dreyer/activemq |
192.168.31.146 | 62623 | 51513 | 8163 | /home/dreyer/activemq |
2、切换至root用户,在各个服务器的防火墙打开对应的端口
# vi /etc/sysconfig/
192.168.31.154中增加:
## activemq -A INPUT -m state --state NEW -m tcp -p tcp --dport 62621 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 51511 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 8161 -j ACCEPT
192.168.31.117中增加:
## activemq -A INPUT -m state --state NEW -m tcp -p tcp --dport 62622 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 51512 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 8162 -j ACCEPT
192.168.31.146中增加:
## activemq -A INPUT -m state --state NEW -m tcp -p tcp --dport 62623 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 51513 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 8163 -j ACCEPT
增加完后,重启防火墙:
# service iptables restart
3、分别在三台机器中创建/home/dreyer/activemq
目录,并上传apache-activemq-5.11.1-bin.tar.gz
到该目录下
$ mkdir /home/dreyer04/activemq
4、解压并按节点进行重命名
$ tar -zxvf apache-activemq-5.11.1-bin.tar.gz
$ mv apache-activemq-5.11.1 node-0X(X代表节点号1、2、3,下同)
5、修改管控台端口(默认为8161),在/conf/jetty.xml
中修改
$ vi /home/dreyer04/activemq/node-02/conf/jetty.xml
node-01管控台端口:
<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start"> <property name="host" value="0.0.0.0"/> <property name="port" value="8161"/> </bean>
node-02管控台端口
<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start"> <property name="host" value="0.0.0.0"/> <property name="port" value="8162"/> </bean>
node-03管控台端口
<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start"> <property name="host" value="0.0.0.0"/> <property name="port" value="8163"/> </bean>
6、集群配置
在3个ActiveMQ节点中配置/conf/activemq.xml
中的持久化配置。修改其中的bind、zkAddress、hostname和zkPath。注意:每个ActiveMQ的BrokerName必须相同,否则不能加入集群。
$ vi /home/dreyer03/activemq/node-01/conf/activemq.xml
(找到brokerName属性并修改,删除原先的persistenceAdapter节点,用下面新的替代)
参数说明:
- directory:数据目录
- replicas:复制节点数
- bind:集群通讯的端口
- zkAddres:zookeeper配置
- hostname:主机名,在/etc/hosts中做的映射
- zkPath:zookeeper数据路径
node-1的持久化配置:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="MyMQ" dataDirectory="${activemq.data}"> <persistenceAdapter> <replicatedLevelDB directory="${activemq.data}/leveldb" replicas="3" bind="tcp://0.0.0.0:62621" zkAddress="192.168.31.154:2181,192.168.31.117:2182,192.168.31.146:2183" hostname="dreyer-zk-01" zkPath="/activemq/leveldb-stores" /> </persistenceAdapter> </broker>
node-01中的消息端口配置(默认为61611,现改为51511):
<transportConnectors> <transportConnector name="openwire" uri="tcp://0.0.0.0:51511?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>
node-02的持久化配置
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="MyMQ" dataDirectory="${activemq.data}"> <persistenceAdapter> <replicatedLevelDB directory="${activemq.data}/leveldb" replicas="3" bind="tcp://0.0.0.0:62622" zkAddress="192.168.31.154:2181,192.168.31.117:2182,192.168.31.146:2183" hostname="dreyer-zk-02" zkPath="/activemq/leveldb-stores" /> </persistenceAdapter> </broker>
node-02中的消息端口配置(默认为61616,现改为51512):
<transportConnectors> <transportConnector name="openwire" uri="tcp://0.0.0.0:51512?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>
node-03的持久化配置
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="MyMQ" dataDirectory="${activemq.data}"> <persistenceAdapter> <replicatedLevelDB directory="${activemq.data}/leveldb" replicas="3" bind="tcp://0.0.0.0:62623" zkAddress="192.168.31.154:2181,192.168.31.117:2182,192.168.31.146:2183" hostname="dreyer-zk-03" zkPath="/activemq/leveldb-stores" /> </persistenceAdapter> </broker>
node-03中的消息端口配置(默认为61616,现改为51513):
<transportConnectors> <transportConnector name="openwire" uri="tcp://0.0.0.0:51513?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>
7、按顺序启动3个ActiveMQ节点
$ /home/dreyer03/activemq/node-01/bin/activemq start
$ /home/dreyer04/activemq/node-02/bin/activemq start
$ /home/dreyer05/activemq/node03/bin/activemq start
监听日志:
$ tail -f /home/dreyer03/activemq/node-01/data/activemq.log
$ tail -f /home/dreyer04/activemq/node-02/data/activemq.log
$ tail -f /home/dreyer05/activemq/node03/data/activemq.log
8、集群的节点状态分析
下载Zooinspector,这是一款zookeeper的监控工具,下载后打开build目录下的zookeeper-dev-ZooInspector,点击绿色的运行按钮,输入zookeeper的连接信息,点击OK即可
从图中我们可以看到ActiveMQ有三个节点,分别是0000000000、0000000001、0000000002
第一张图的elected的值不为null,说明这个节点是master,其他两个elected为null的节点为slave。
9、集群的可用性测试(配合应用程序,略)
ActiveMQ 的客户端只能访问 Master 的 Broker,其他处于 Slave 的 Broker 不能访问。所以客户端连接 Broker 应该使用 failover(失效转移)协议。
failover:(tcp://192.168.1.154:51511,tcp://192.168.31.117:51512,tcp://192.168.31.146:51513)?randomize=false
附项目中mq.properties配置信息
## MQ
mq.brokerURL=failover:(tcp://192.168.31.154:51511,tcp://192.168.31.117:51512,tcp://192.168.31.146:51513)?randomize=false
mq.userName=admin
mq.password=admin
mq.pool.maxConnetcions=10
##queueName
queueName=dreyer.mq.v1
10、集群的高可用性
当一个 ActiveMQ 节点挂掉,或者一个 ZooKeeper 节点挂掉,ActiveMQ 服务依然正常运转。当主节点挂掉,ActiveMQ会很快又选举出一个主节点,如果仅剩一个 ActiveMQ 节点,因为不能选举 Master,ActiveMQ 不能正常运转;同样的,如果 ZooKeeper 仅剩一个节点活动,不管 ActiveMQ 各节点是否存活,ActiveMQ 也不能正常提供服务。
(ActiveMQ 集群的高可用,依赖于 ZooKeeper 集群的高可用 ,整个zookeeper集群挂掉之后,需要对ActiveMQ进行重启)
11、设置ActiveMQ开机启动
切换至root用户编辑/etc/rc.local文件
# vi /etc/rc.local
三台机器分别添加以下脚本(脚本大意:切换至dreyer03用户,执行xx目录下的启动脚本)
su - dreyer03 -c '/home/dreyer03/activemq/node-01/bin/activemq start'
su - dreyer04 -c '/home/dreyer04/activemq/node-02/bin/activemq start'
su - dreyer05 -c '/home/dreyer05/activemq/node-03/bin/activemq start'
最后,附上官方文档的一则警告,请使用者注意。replicatedLevelDB 不支持延迟或者计划任务消息。这些消息存储在另外的 LevelDB 文件中,如果使用延迟或者计划任务消息,将不会复制到 slave Broker 上, 不能实现消息的高可用。