activemq+Zookeper高可用集群方案配置
在高并发、对稳定性要求极高的系统中,高可用的是必不可少的,。从ActiveMQ 5.9开始,ActiveMQ的集群实现方式取消了传统的Master-Slave方式,增加了基于ZooKeeper + LevelDB 的 Master-Slave 实现方式。
ActiveMQ的高可用原理
- 使用ZooKeeper(集群)注册所有的ActiveMQ Broker。只有其中的一个Broker可以提供服务,被视为 Master,其他的 Broker 处于待机状态,被视为Slave。如果Master因故障而不能提供服务,Zookeeper会从Slave中选举出一个Broker充当Master。
- Slave连接Master并同步他们的存储状态,Slave不接受客户端连接。所有的存储操作都将被复制到 连接至 Master的Slaves。如果Master宕了,得到了最新更新的Slave会成为 Master。故障节点在恢复后会重新加入到集群中并连接Master进入Slave模式。(Redis Sentinel主从高可用的方式很像,这里的zookeeper起到的作用和reids里的sentinel作用差不多)
- 另外,附上官方文档的一则警告,请使用者注意。replicated LevelDB 不支持延迟或者计划任务消息。这 些消息存储在另外的LevelDB文件中,如果使用延迟或者计划任务消息,将不会复制到Slave Broker上,不能实现消息的高可用。
ActiveMQ集群配置
环境准备
- 准备zookeeper 集群 3台
- 在在3台服务器安装好 ActiveMQ
修改配置:修改每一台 acrivemq 的 activemq.xml 文件的<persistenceAdapter>
标签
机器1:
<persistenceAdapter>
<replicatedLevelDB
directory="${activemq.data}/leveldb" #数据存储路径
replicas="3" #节点个数
bind="tcp://0.0.0.0:0" #用于各个节点之间的通讯
zkAddress="192.168.72.129:2181,192.168.72.130:2181,192.168.72.131:2181"
hostname="192.168.72.129"
zkPath="/activemq/leveldb-stores"/>#在zookeeper中集群相关数据存放路径
</persistenceAdapter>
<!--
# directory: 存储数据的路径
# replicas:集群中的节点数【(replicas/2)+1公式表示集群中至少要正常运行的服务数量】,3台集群那么允许1台宕机, 另外两台要正常运行
# bind:当该节点成为master后,它将绑定已配置的地址和端口来为复制协议提供服务。还支持使用动态端口。只需使用tcp://0.0.0.0:0进行配置即可,默认端口为61616。
# zkAddress:ZK的ip和port, 如果是集群,则用逗号隔开(这里作为简单示例ZooKeeper配置为单点, 这样已经适用于大多数环境了, 集群也就多几个配置)
# zkPassword:当连接到ZooKeeper服务器时用的密码,没有密码则不配置。
# zkPah:ZK选举信息交换的存贮路径,启动服务后actimvemq会到zookeeper上注册生成此路径
# hostname: ActiveMQ所在主机的IP
# 更多参考:http://activemq.apache.org/replicated-leveldb-store.html
-->
机器2:
<persistenceAdapter>
<replicatedLevelDB
directory="${activemq.data}/leveldb" #数据存储路径
replicas="3" #节点个数
bind="tcp://0.0.0.0:0" #用于各个节点之间的通讯
zkAddress="192.168.72.129:2181,192.168.72.130:2181,192.168.72.131:2181"
hostname="192.168.72.130"
zkPath="/activemq/leveldb-stores"/>#在zookeeper中集群相关数据存放路径
</persistenceAdapter>
<!--
# directory: 存储数据的路径
# replicas:集群中的节点数【(replicas/2)+1公式表示集群中至少要正常运行的服务数量】,3台集群那么允许1台宕机, 另外两台要正常运行
# bind:当该节点成为master后,它将绑定已配置的地址和端口来为复制协议提供服务。还支持使用动态端口。只需使用tcp://0.0.0.0:0进行配置即可,默认端口为61616。
# zkAddress:ZK的ip和port, 如果是集群,则用逗号隔开(这里作为简单示例ZooKeeper配置为单点, 这样已经适用于大多数环境了, 集群也就多几个配置)
# zkPassword:当连接到ZooKeeper服务器时用的密码,没有密码则不配置。
# zkPah:ZK选举信息交换的存贮路径,启动服务后actimvemq会到zookeeper上注册生成此路径
# hostname: ActiveMQ所在主机的IP
# 更多参考:http://activemq.apache.org/replicated-leveldb-store.html
-->
机器3:
<persistenceAdapter>
<replicatedLevelDB
directory="${activemq.data}/leveldb"
replicas="3"
bind="tcp://0.0.0.0:0"
zkAddress="192.168.72.129:2181,192.168.72.130:2181,192.168.72.131:2181"
hostname="192.168.72.131"
zkPath="/activemq/leveldb-stores"/>
</persistenceAdapter>
<!--
# directory: 存储数据的路径
# replicas:集群中的节点数【(replicas/2)+1公式表示集群中至少要正常运行的服务数量】,3台集群那么允许1台宕机, 另外两台要正常运行
# bind:当该节点成为master后,它将绑定已配置的地址和端口来为复制协议提供服务。还支持使用动态端口。只需使用tcp://0.0.0.0:0进行配置即可,默认端口为61616。
# zkAddress:ZK的ip和port, 如果是集群,则用逗号隔开(这里作为简单示例ZooKeeper配置为单点, 这样已经适用于大多数环境了, 集群也就多几个配置)
# zkPassword:当连接到ZooKeeper服务器时用的密码,没有密码则不配置。
# zkPah:ZK选举信息交换的存贮路径,启动服务后actimvemq会到zookeeper上注册生成此路径
# hostname: ActiveMQ所在主机的IP
# 更多参考:http://activemq.apache.org/replicated-leveldb-store.html
-->
启动测试
-
启动zookeeper 集群
-
在安装目录下的bin目录,分别启动每台activemq
./activemq start
-
查看zookeeper 集群下的节点情况
在zookeeper的bin目录下使用./zkCli.sh
连接zookeeper。
使用命令 :ls /activemq/leveldb-stores
查看节点情况:
命令:get /activemq/leveldb-stores/XXXXXXXXX
查看具体情况,
-
使用java代码连接activemq集群,并进行宕机验证(故障迁移验证)。
客户端:public class ActivemqProducer { static String BROCKER_URL = "failover:(tcp://192.168.72.129:61616,tcp://192.168.72.130:61616,tcp://192.168.72.131:61616)"; static String QUEUE_NAME = "cluster-queue"; public static void main(String[] args) throws JMSException { // 1. 创建连接工厂 ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(BROCKER_URL); // 2. 获取连接 并启动 Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); // 3. 创建session 并开启事务,参数一:事务,参数二:签收 Session session = connection.createSession(true, Session.SESSION_TRANSACTED); //4. 创建目的地,队列 Queue queue = session.createQueue(QUEUE_NAME); // 5.创建消息生产者 MessageProducer messageProducer = session.createProducer(queue); try { // 6.通过 messageProducer 发送3条消息 for (int i = 1; i <= 3; i++) { TextMessage textMessage = session.createTextMessage("消息 ->" + i); messageProducer.send(textMessage); } // 提交事务 session.commit(); } catch (JMSException e) { // 回滚 session.rollback(); e.printStackTrace(); } System.out.println("发送消息完成......"); // 7.关闭资源 messageProducer.close(); session.close(); connection.close(); } }
运行代码:此时消息能够正常发送。也能正常接收,
我们故意停掉 129 机器,在此进行验证,
运行代码,依然可以正常的发送和接收消息。