ActiveMQ多节点集群_16

一、Zookeeper与Replicated LevelDB集群原理

面试题:引入消息队列后,如何保证高可用性?

基于Zookeeper和LevelDB搭建的ActiveMQ集群,集群提供主备方式的高可用集群功能,避免单点故障。

ActiveMQ官网主从介绍:http://activemq.apache.org/masterslave.html

可以看到,这里支持3种方式:共享文件系统主从、JDBC主从、可复制的LevelDB存储。

LevelDB是ActiveMQ 5.6版本之后推出的持久化引擎,它使用了自定义的索引代替常用的BTree索引,其持久化性能高于KahaDB,虽然默认方式还是KahaDB,但是LevelDB在未来可能会成为趋势。

在ActiveMQ 5.9版本还提供了基于LevelDB和Zookeeper的数据复制方式,作为Master-Slave方式的首选数据复制方案。

共享文件系统主从:http://activemq.apache.org/shared-file-system-master-slave

JDBC主从:http://activemq.apache.org/jdbc-master-slave

可复制的LevelDB存储:http://activemq.apache.org/replicated-leveldb-store.html

主要来说的是可复制的LevelDB存储。

原理说明:

使用Zookeeper集群注册所有的ActiveMQ Broker,但是只要一个Broker可以提供服务,被视为Master,其他的Broker处于待机状态被视为Slave。

如果Master因为故障不能提供服务了,Zookeeper会从Slave中选出一个Broker充当Master。在运行过程中,Slave通过连接Master,来同步它们的存储状态,Slave不接受客户端连接。所有的存储操作都将被复制到连接至Master的Slave中。如果Master宕机,得到最新更新的Slave会成为Master。故障结点在恢复后重新加入进群中作为Slave连接Master。

所有需要同步的消息操作都将等待存储状态被复制到其他法定结点操作完成才能完成。

如果配置了replicas=3,那么法定节点是(3/2)+1=2个。Master会存储并更新,然后等待(2-1)=1个Slave存储和更新完成,才汇报success。

二、Zookeeper与Replicated LevelDB集群部署规划说明

Zookeeper下载地址:https://zookeeper.apache.org/releases.html点击Download,选择镜像地址下载,这里我下载的是Zookeeper-3.4.6版本。

搭建单机伪集群,当然你也可以搭建真集群。为什么说叫伪集群呢?因为3台Zookeeper搭建在了一台虚拟机上, 用端口号来区分,真集群的话,就是3台Zookeeper位于3台机器上,废话不多说,我们开始吧。

下载好Zookeeper后,使用tar -zxvf apache-zookeeper-3.4.6.tar.gz解压到一个目录里,本人的是在根目录下创建了 一个/zk_cluster目录,执行mv apache-zookeeper-3.4.6 zookeeper01修改目录名,在conf目录下,创建一个zoo.cfg文件,在zookeeper01中创建data文件夹和dataLog文件夹。把zookeeper01复制出来两份分别是zookeeper02和zookeeper03,此时,在zookeeper文件夹下,就有zookeeper01,zookeeper02,zookeeper03这3台zookeeper了,我们下面对这3台zookeeper的zoo.cfg进行更改。
 

Zookeeper端口ActiveMQWeb端口ActiveMQ协议端口
2181816161616
2182816261617
2183816361618

 

zookeeper01的zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
clientPort=2181
dataDir=/usr/zookeeper/zookeeper01/data
dataLogDir=/usr/zookeeper/zookeeper01/datalogs
server.1=192.168.0.123:2881:3881
server.2=192.168.0.123:2882:3882
server.3=192.168.0.123:2883:3883
 
zookeeper02的zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
clientPort=2182
dataDir=/usr/zookeeper/zookeeper02/data
dataLogDir=/usr/zookeeper/zookeeper02/datalogs
server.1=192.168.0.123:2881:3881
server.2=192.168.0.123:2882:3882
server.3=192.168.0.123:2883:3883
 
zookeeper03的zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
clientPort=2183
dataDir=/usr/zookeeper/zookeeper03/data
dataLogDir=/usr/zookeeper/zookeeper03/datalogs
server.1=192.168.0.123:2881:3881
server.2=192.168.0.123:2882:3882
server.3=192.168.0.123:2883:3883

指定myid,进入上面指定的dataDir目录,新建一个myid文件,写入id值,分别是1,2,3,也就是server.后面的值,相当于给zookeeper编号了。

然后就可以启动Zookeeper了。分别在3个文件的bin目录下执行./zkServer.sh start命令,启动3台zookeeper。

另外提供一个命令:./zkServer.sh start-foreground可以直接把报错信息打印出来,方便排查问题。

这里说一个情况,因为配置了集群,单独启动一台的时候,用status查看状态,会提示Error contacting service. It is probably not running.这个错误,但是当3台都启动起来最后,再去使用status查看状态,就正常了,可能是单独启动一台的时候,读取配置文件中的其他server读取不到,导致的问题吧。

然后写三个脚本,通过一个命令直接启动3台zookeeper,关闭3台zookeeper,查看3台zookeeper的状态,不用来回切换目录操作,减少麻烦。

启动(使用命令vim start-zookeeper-all.sh创建文件)

#!/bin/sh
cd /usr/zookeeper/zookeeper01/bin
./zkServer.sh start
cd /usr/zookeeper/zookeeper02/bin
./zkServer.sh start
cd /usr/zookeeper/zookeeper03/bin
./zkServer.sh start

关闭(使用命令vim stop-zookeeper-all.sh创建文件)

#!/bin/sh
cd /usr/zookeeper/zookeeper01/bin
./zkServer.sh stop
cd /usr/zookeeper/zookeeper02/bin
./zkServer.sh stop
cd /usr/zookeeper/zookeeper03/bin
./zkServer.sh stop

查看状态(使用命令vim status-zookeeper-all.sh创建文件)

#!/bin/sh
cd /usr/zookeeper/zookeeper01/bin
./zkServer.sh status
cd /usr/zookeeper/zookeeper02/bin
./zkServer.sh status
cd /usr/zookeeper/zookeeper03/bin
./zkServer.sh status

然后使用命令chmod u+x start-zookeeper-all.sh,chmod u+x stop-zookeeper-all.sh,chmod u+x status-zookeeper-all.sh给这3个文件加上运行权限,然后只需要执行start-zookeeper-all.sh或stop-zookeeper-all.sh或status-zookeeper-all.sh就可以完成功能了。

至此,伪集群部署已经完成,3台zookeeper已经开始可以正常工作了,通过status可以看到哪个是master,哪个是follower。

三、Zookeeper与Replicated LevelDB集群部署配置

创建/mq_cluster目录,使用cp -r apache-activemq-5.15.11 activemq01复制一份,再重复2次,分别复制出来activemq02和activemq03。

此时,3个文件夹并列位于activemq目录下。

修改管理控制台的端口,进入conf目录下,找到jetty.xml文件,修改jettyPort结点的port属性,将activemq02里的改为8162,同理,将activemq03里的改为8163。

<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
         <!-- the default port number for the web console -->
    <property name="host" value="0.0.0.0"/>
    <property name="port" value="8161"/>
</bean>

host名字映射  vim /etc/host  这里叫 activemqCluster

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.5.96.57  activeCluster

MQ集群的配置,将3个文件夹中activemq.xml的配置文件中brokerName改成一样的 

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

配置集群的持久化,分别进入三个文件的进入conf目录下,编辑activemq.xml。

把persistenceAdapter结点替换成如下内容,需要将三个文件中的配置都做修改。注意bind里面的端口不要重复,这里分别采用63631,63632,63633。

 <replicatedLevelDB directory="${activemq.data}/leveldb" 
                         replicas="3" 
                         bind="tcp://0.0.0.0:63631" 
                         zkAddress="10.5.96.48:2181,10.5.96.48:2182,10.5.96.48:2183" 
                         hostname="activemqCluster" 
                         sync="local_disk" 
                         zkPath="/activemq/leveldb-stores"/>
 </persistenceAdapter>

下面来说下参数属性的含义:

          directory:数据存放的目录,存储在data目录下的leveldb下。

          replicas:结点的个数是3个。

          bind:绑定的地址。

           zkAddress:zookeeper集群的地址和端口号,就是之前搭建的zookeeper集群的地址。

           hostname:ip地址。

           sync:同步在本地磁盘。

           zkPath:如果activemq被zookeeper管理起来后,将会在这个路径下写leveldb存储。
 

先启动zookeeper集群,后启动activemq集群。

参考前面vim start-zookeeper-all.sh的样子,写一个start-activemq-all.sh和stop-activemq-all.sh和status-activemq-all.sh脚本方便运行。

可以使用ps -ef | grep zookeeper和ps -ef | grep activemq来验证是否启动成功,如果说zookeeper有3个进程号,activemq有3个进程号,那么就是启动成功了。

进入到任意一台zookeeper的bin目录下,执行./zkCli.sh -server 127.0.0.1:2181连接一台zookeeper查看下是否注册成功ActiveMQ。

再使用ls /命令,查看注册到zookeeper的服务,可以发现有一个activemq,这个activemq对应着activemq.xml里的zkPath,如果这里的zkPath是其他内容,这里会显示其他内容,当然,也可以使用命令ls /activemq查看activemq下的内容,根据上面的配置,可以知道activemq下有一个leveldb-stores的文件夹,使用ls /activemq/leveldb-stores,可以看到有3个数据,这就是activemq的3个结点了。

本人的集群截图

查看那个ActiveMQ服务是master呢?继续在刚才的会话中操作,使用命令:get /activemq/leveldb-stores/00000000006,修改最后的06为07和08,分别查看输出,会看到如下内容,关注第一条的elected的值,发现它不是null,那么这个结点就是ActiveMQ的master了,其余就是slave。

四、 Replicated LevelDB集群故障迁移和验证

ActiveMQ的客户端只能访问Master的Broker,其他处于Slave的Broker不能访问,所以客户端连接的Broker应该使用failover协议(失败转移协议)。

使用命令lsof -i:端口号,查看ActiveMQ的运行情况,因为前面我们配置的端口号分别是8161,8162,8163,所以这里带的端口号也是这几个,发现有一个命令是有返回结果的,其他两个没有。然后使用10.5.96.48:端口号/admin访问,这里我使用0.5.96.48:8163/admin访问,可以访问到管理页面。尝试8161和8162端口,发现不能访问,此时,ActiveMQ的集群中,只有8163端口下的master可以访问,其余的不能访问,但是数据上保持同步。

当一个ActiveMQ结点挂掉或者一个Zookeeper结点挂掉,ActiveMQ服务依然正常运转,如果仅剩一个ActiveMQ结点,由于不能选举master,所以ActiveMQ不能正常运行。

同样,如果Zookeeper仅剩一个结点活动,不管ActiveMQ结点是否存活,ActiveMQ也不能正常提供服务,因为ActiveMQ集群的高可用依赖于Zookeeper集群的高可用。

修改Producer和Consumer的代码,将ACTIVEMQ_URL属性改为failover协议,代码如下
生产者

public class JmsProduce {

    public static final String ACTIVEMQ_URL = "failover:(tcp://10.5.96.48:61616,tcp://10.5.96.48:61617,tcp://10.5.96.48:61618)?randomize=false";
    public static final String QUEUE_NAME = "active-cluster";

    public static void main(String[]args) throws JMSException {
        //1.创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过连接工厂,获得连接connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话 session
        //两个桉树 ,第一个叫事务  第二个叫签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4 .创建目的地(具体是队列还是主题topic)
        Queue queue = session.createQueue(QUEUE_NAME);
        //5. 创建消息的生产者
        MessageProducer messageProducer = session.createProducer(queue);
        //消息的持久化设置(现在是非持久)
        messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
        //6.通过使用messageProducer 生产3条消息发送到队列里面
        for (int i = 0; i <3 ; i++) {
            // 7.创建消息
            TextMessage textMessage = session.createTextMessage("Msg---" + i);
            //8.消息生产者发送给mq
            messageProducer.send(textMessage);
        }
        //9.关闭资源 顺着开启,倒着关闭
        messageProducer.close();
        session.close();
        connection.close();

        System.out.println("**** 消息发布到MQ完毕 ****");
    }

}

消费者

public class JmsConsumer {

    public static final String ACTIVEMQ_URL = "failover:(tcp://10.5.96.48:61616,tcp://10.5.96.48:61617,tcp://10.5.96.48:61618)?randomize=false";
    public static final String QUEUE_NAME = "active-cluster";

    public static void main(String[]args) throws JMSException, IOException {
        System.out.println("我是2号消费者");
        //1.创建连接工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过连接工厂,获得连接connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话 session
        //两个参数 ,第一个叫事务  第二个叫签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地(具体是队列还是主题Topic)
        Queue queue = session.createQueue(QUEUE_NAME);
        //创建消费者
        MessageConsumer messageConsumer = session.createConsumer(queue);
       
        while(true){
            TextMessage textMessage =  (TextMessage) messageConsumer.receive(4000);
            if( null !=textMessage ){
                System.out.println("***消费者受到消息:" + textMessage.getText());
            }else{
                break;
            }
        }
        messageConsumer.close();
        session.close();
        connection.close();
    }
}

 然后启动生产者发送消息。再去管理页面查看,可以发现有消息已经发送成功了。再启动消费者,之后消息被消费者消费掉了。此时一切正常。

下面,我们尝试将某一台ActiveMQ干掉,模拟ActiveMQ的故障。使用命令kill -9 进程号杀死ActiveMQ进群的master进程。假设这里杀死了8163这台机器,那么经过Zookeeper的选举后,8161或8162有一个会变成master结点。刚才访问的是8163 证明的是第三个进程是master ,所以干掉master端口,然后zookeeper会选出一个master 下图,访问8162,证明集群是成功的

使用start-activemq-all.sh启动ActiveMQ集群的时候,会启动失败,不知道为什么,有时候需要重试几次才能都启动起来。

另外,还需要检查Zookeeper中activemq结点里的address是不是null,如果是master的address是null,那么也访问不到,仔细检查配置,实在不行就重头再来。
 

参考

https://blog.csdn.net/qq_36059561/article/details/103915642     

https://blog.csdn.net/qq_36059561/article/details/103916980     

https://blog.csdn.net/qq_36059561/article/details/103965376    

https://blog.csdn.net/qq_36059561/article/details/103965896

https://blog.csdn.net/qq_36059561/article/details/103982080

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值