ActiveMQ集群Master-Slave KahaDB +Broker Cluster

在生产环境中,ActiveMQ作为消息中间件,保证消息的安全、稳定和可靠必不可少,高可用集群有两个两种模式:Master-Slave和broker Cluster。

集群部署架构主要有三种模式,共享文件系统主从、JDBC主从和复制LevelDB存储(弃用)推荐kahaDB(另外Pure Master-Slave 这种shared nothing 架构在5.8版本之后已经不支持了,主要原因是Master挂了之后再起来无法自动融入集群,需要停掉MQ重新启动才可以,本次讨论略过)。

一,集群模式概述
集群模式主要是为了解决ActiveMQ系统架构中的两个关键问题:高可用和高性能。
针对上述两种需求,AActiveMQ主要有如下两种集群模式分别对应:

  1. Master-slave模式
  2. Broker-Cluster模式

Master-Slave模式
Master-Slave集群由至少3个节点组成,一个Master节点,其他为Slave节点。
只有Master节点对外提供服务,Slave节点处于等待状态。当主节点宕机后,从节点会推举出一个节点出来成为新的Master节点,继续提供服务。
在这里插入图片描述
Master-Slave模式的部署方式,主要分为三种:
Shared Filesystem Master-Slave方式,如KahaDB,应用灵活、高效且安全。
Shared Database Master-Slave方式,基于共享数据库,跟第一种类似,性能会受限于数据库。
Replicated LevelDB Store方式,基于zookeeper+leveldb是生产环境常用的方案,**被弃用 **。

Broker-Cluster模式
Broker-Cluster部署方式中,各个broker通过网络互相连接,自动分发调用端请求,从而实现集群的负载均衡。Broker-Cluster集群连接到网络代理的方式,主要分为静态网络代理、动态网络代理,不同的网络代理方式也对应了不同的集群部署方式:

1. static Broker-Cluster
2. Dynamic Broker-Cluster

static Broker-Cluster实际应用场景被广泛使用
在这里插入图片描述
集群架构
Master-Slave集群的优点是可以解决多服务热备的高可用问题,但缺点是无法解决负载均衡和分布式的问题。Broker-Cluster集群的优点是可以解决负载均衡和分布式的问题,但不支持高可用。

因此,把Master-Slave和Broker-Cluster两者相结合,就可以得到一个完美的解决方案:即又可以做到集群负载均衡又可以做到任何一个broker如果发生宕机,也不会影响提供服务,节点消息也不会“丢失”。

这里我们选择部署4个ActiveMQ实例,把4个ActiveMQ实例分成两组(Group)。Group1的2个节点通过Master-Slave模式组成brokerA,group2的2个节点通过Master-Slave模式组成brokerB,并使得brokerA与brokerB组成Broker-Cluster集群。
在这里插入图片描述
在这里插入图片描述
二,地址规划和安装

节点 IP地址 端口 软件包
host1 192.168.110.128 admin:8161 openwire:61616 ActiveMQ Java
host2 192.168.110.129 admin:8162 openwire:61617 ActiveMQ Java
host3 192.168.110.130 admin:8163 openwire:61618 ActiveMQ Java
host4 192.168.110.131 admin:8164 openwire:61619 ActiveMQ Java

2.1 准备工作
1.修改IP地址为静态
2.修改hosts映射主机名及关闭selinux
3.测试主机名互通和是否可以上网
4.安装基本软件包

yum install java-1.8.0-openjdk* -y
yum install net-tools wget lrzsz lsof epel-release vim bash-completion make cmake gcc gcc-c++  screen nfs-utils -y

2.2安装ActiveMQ节点
1.安装java

#可在线安装或源码二进制安装
yum install java-1.8.0-openjdk* -y

2.下载activeMQ二进制安装软件包,创建用户
官网地址: http://activemq.apache.org/

wget http://activemq.apache.org/path/tofile/apache-activemq-5.15.12-bin.tar.gz

3.解压并安装

tar -zxvf apache-activemq-5.15.12-bin.tar.gz
mv apache-activemq-5.15.12 /usr/local/
ln -s /usr/local/apache-activemq-5.15.12/ /usr/local/apache-activemq

4.解释相关目录文件及修改

目录:
bin:服务启动相关的命令文件所在目录
conf:配置文件所在目录,任何配置文件修改后,都要重启ActiveMQ,否则不生效。
data:默认持久化文件所在目录(日志)
docs:里面放的是用户手册
examples:存放例子,包括配置文件,代码
lib:存放jar包
webapps:管理台的应用
activemq-all-5.15.12.jar:客户端连接包
conf目录下需要关注的文件:activemq.xml,jetty.xml,users.properties
activemq.xml:spring配置文件,配置的是activemq应用使用的默认对象组件,配置安全认证和持久化都在这个文件中。
jetty.xml:spring配置文件,activemq使用的是jetty提供http服务,该文件用于配置jetty服务器的默认对象组件。端口号等的配置在这个文件中。
groups.properties:用户组=用户1,用户2(多个用户中间用逗号隔开)
users.properties:用户名和密码修改的地方  格式为用户名=密码
jetty-realm.properties:定义可以访问Web的用户(console, demo, etc.)
#查找java目录位置
whereis java
#添加到activemq的env中.
JAVA_HOME="/usr/java/jdk1.8.0_191/bin/java"
JAVACMD="auto"

5.创建ActiveMQ的systemd服务
systemd默认从目录/etc/systemd/system读取配置文件。但是,里面存放的大部分文件都是符号链接,指向目录/usr/lib/systemd/system/,真正的配置文件放在这个目录中.

vim activemq.service
#添加如下内容:
[Unit]		#启动顺序与依赖关系
Description=Apache ActiveMQ	#当前服务的描述
After=network-online.target	#表示如果network.target或sshd-keygen.service需要启动,那么sshd.service应该在它们后面启动.

[Service]	#启动行为
Type=forking	#启动类型
PIDFile=/usr/local/apache-activemq/data/activemq.pid	#PID路径
ExecStart=/usr/local/apache-activemq/bin/activemq start	#启动进程时执行的命令
ExecStop=/usr/local/apache-activemq/bin/activemq stop	#停止服务时执行的命令
Restart=on-failure	#重启方式
RestartSec=42s		#systemd重启等待时间
User=root		#用户
Group=root		#组
tandardOutput=syslog	#进程标准输出,表示日志服务,默认复制一份到journal
StandardError=syslog	#和tandardOutput类似
SyslogIdentifier=activemq	#日志标识符(activemq logs)

[Install]
WantedBy=multi-user.target

6.重载并启动,查看端口

systemctl daemon-reload
systemctl enable activemq.service
systemctl start activemq.service
systemctl status activemq.service
[root@hopeking apache-activemq]# netstat -anput | grep 61616
tcp6       0      0 :::61616                :::*                    LISTEN      24705/java          
[root@hopeking apache-activemq]# netstat -anput | grep 8161
tcp6       0      0 :::8161                 :::*          

相关端口: 8161:WEB端口 61616:通讯端口

6.防火墙添加相关端口,不同节点开放不同端口

firewall-cmd --zone=public --add-port=8161/tcp --permanent
firewall-cmd --zone=public --add-port=61616/tcp --permanent
firewall-cmd --reload
#查看端口是否设置成功
firewall-cmd --zone=public --list-ports

浏览器访问:http://IP:8161/admin 用户admin 密码admin

#禁用IPV6
vim /etc/sysctl.conf
#添加如下内容:
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
#执行
sysctl -p

三,Master-Slave配置
1.安装NFS和rpcbind服务
将110.129上面的数据目录共享给110.128上的activemq服务

yum install nfs-utils -y
systemctl enable rpcbind
systemctl enable nfs
systemctl start rpcbind
systemctl start nfs

firewall-cmd --zone=public --permanent --add-service={
    
    rpc-bind,mountd,nfs}
firewall-cmd --reload

修改nfs的共享配置文件:/etc/exports

# 文件路径  
/usr/local/apache-activemq/data/kahadb 192.168.110.0/24(rw,sync,no_root_squash,no_all_squash)
#相关解释
/usr/local/apache-activemq/data/kahadb: 共享目录位置。
192.168.110.0/24: 客户端 IP 范围,* 代表所有,即没有限制。
rw: 权限设置,可读可写。
sync: 同步共享目录。
no_root_squash: 可以使用 root 授权。
no_all_squash: 可以使用普通用户授权。
sudo systemctl restart nfs
#检查一下本地的共享目录
$ showmount -e localhost
Export list for localhost:
/usr/local/apache-activemq-5.15.12/data/kahadb 192.168.110.0/24
[root@host1 ~]# mount -t nfs 192.168.110.129:/usr/local/apache-activemq/data/kahadb/ /usr/local/apache-activemq/data/kahadb/
[root@host1 ~]# df -hT
192.168.110.129:/usr/local/apache-activemq/data/kahadb/ nfs        17G  2.4G   15G   14% /usr/local/apache-activemq-5.15.12/data/kahadb

当然,最后将该目录自动挂载,避免系统重启后需手动挂载。自动挂载即修改配置文件/etc/fstab
开机自动挂载配置

sudo vi /etc/fstab

#
# /etc/fstab
# Created by anaconda on Fri Sep 21 08:26:38 2018
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=9972b3db-8159-493a-9d04-3dd1ed93e2f4 /boot                   xfs     defaults        0 0
/dev/mapper/centos-swap swap                    swap    defaults        0 0
192.168.110.129:/usr/local/apache-activemq/data/kahadb /usr/local/apache-activemq/data/kahadb nfs defaults 0 0

ActiveMQ Master-Slave集群的broker必须有统一的brokername,在这里需要修改ACTIVEMQ_HOME/conf/activemq.xml文件,将2个节点的brokerName统一为“brokerA”。

[root@host1 ~]# vim /usr/local/apache-activemq/conf/activemq.xml
如下内容:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="brokerA" persistent="true" dataDirectory="${activemq.data}">

ActiveMQ的持久化主要有如下几种方案:
基于日志文件存储,如KahaDB
JDBC消息存储,如使用mysql将消息持久化
LevelDB消息存储 #弃用

这里采用KahaDB的方式,原理就是让参与高可用的所有节点共用一个数据文件目录,通过文件锁的方式来决定谁是master谁是slave。在这里需要做的就是将2个节点的数据目录配置成相同的即可。

修改ACTIVEMQ_HOME/conf/activemq.xml文件,2个节点均修改成如下配置即可。

<persistenceAdapter>
     <kahaDB directory="/usr/local/apache-activemq/data/kahadb"/>
</persistenceAdapter>

2.端口配置
暂时只用到tcp,所以只配置openwire,暂时将其它的端口配置注释掉。openwire默认为61616,端口不能重复,否则端口冲突,所以需要将host1,host2,host3,host4分别对应端口61616,61617,61618,61619。

该配置也在ACTIVEMQ_HOME/conf/activemq.xml, 具体修改如下:

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

3.Jetty端口配置
ActiveMQ使用jetty运行服务,与openwire同理,4个节点运行的端口必须不同。修改ACTIVEMQ_HOME/conf/jetty.xml,4个节点依次改为8161,8162,8163,8164。

<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>
<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="8162"/>
</bean>
<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="8163"/>
</bean>
<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="8164"/>
</bean>

4.启动主从

systemctl start activemq.service

如果配置正常,在后启动的两个节点的启动日志中会输出如下日志,表示已经有master锁定,自己将以slave角色运行。
注意:只有master节点接受请求,slave不接受请求,也无法使用管理界面。
192.168.110.128:

| INFO  | ActiveMQ Jolokia REST API available at http://0.0.0.0:8161/api/jolokia/ | org.apache.activemq.web.WebConsoleStarter | main

192.168.110.129:

| INFO  | Database /usr/local/apache-activemq/data/kahadb/lock is locked by another server. This broker is now in slave mode waiting a lock to be acquired | org.apache.activemq.store.SharedFileLocker | main

PS:host3和host4与前面配置大同小异根据前面进行配置就可以

四,搭建Broker-Cluster集群
链接地址:http://activemq.apache.org/networks-of-brokers.html
分为:
Static Broker-Cluster 静态发现
Dynamic Broker-Cluster 动态发现
MasterSlave Discovery 主从发现

Static Broker-Cluster
在 ${ACTIVEMQ_HOME}/conf/activemq.xml 中静态指定 Broker 需要桥连接的其他 Broker。
在所有节点中添加 networkConnector 节点,uri 地址为集群内其他节点的信息。

<networkConnectors>
    <networkConnector uri="static:(tcp://other-broker:port)" duplex="false"/>
</networkConnectors>

所有的 broker 都启动后,在 web 管理界面的 Network 页面可以看到连接信息。
注意:static静态连接只提供master连接,从连接会被拒绝因为从连接只提供给master连接,拒绝其他连接

Dynamic Broker-Cluster
在 ${ACTIVEMQ_HOME}/conf/activemq.xml 中静态指定 Broker 需要桥连接的其他 Broker。由 activemq 启动后动态查找。(基于 multicast 技术,确保网络畅通)
1.先在 Broker 节点中添加 networkConnector 节点。

<networkConnectors>
    <networkConnector uri="multicast://default"/>
</networkConnectors>

2.修改 transportConnector,增加 discoveryUri 属性,并添加 publishedAddressPolicy。

<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600" discoveryUri="multicast://default">
    <publishedAddressPolicy publishedHostStrategy="IPADDRESS">
    </publishedAddressPolicy>
</transportConnector>

MasterSlave Discovery

<networkConnectors>
  <networkConnector uri="masterslave:(tcp://host1:61616,tcp://host2:61616,tcp://..)"/>
</networkConnectors>

列出URI的顺序为:MASTER,SLAVE1,SLAVE2…SLAVE,相同的static:配置选项可用于masterslave:

PS:这里我直接在当前activemq节点做Broker-Cluster了,建议使用额外的activemq节点来做,

Broker-Cluster部署方式中,“静态方式”在实际应用场景中被大量使用,因此我们这里采用静态uri方式,具体操作如下所示。

1.在group1中所有的节点修改ACTIVEMQ_HOME/conf/activemq.xml文件,添加如下配置链接group在(persistenceAdapter标签前配置):

<networkConnectors>
    #主从,所有activemq节点都配置
    <networkConnector uri="masterslave:(tcp://192.168.110.130:61618,tcp://192.168.110.131:61619)" duplex="false" />
    #静态brokerA配置就可以了
    <networkConnector name="brokerB" uri="static:(tcp://192.168.110.130:61618,tcp://192.168.110.131:61619)" duplex="true" />
</networkConnectors>

需要注意,broker-cluster模式duplex默认为false,这种情况下broker-cluster可以实现消息分发,但消息只存在一个broker上。与此同时,也就意味着,一旦broker宕机,则可能会出现消息丢失。

这里我们配置duplex=“true”,当这个节点使用Network Bridge连接到其它目标节点后,将强制目标也建立Network Bridge进行反向连接。其目的在于让消息既能发送到目标节点,又可以通过目标节点接受消息。它相当于下面几行的效果:

<!-- 在group1中 -->  
<networkConnector name="group1-broker3" uri="static:(tcp://192.168.110.130:61618)" duplex="false" />  
<!-- 在group1中 -->  
<networkConnector name="group1-broker4" uri="static:(tcp://192.168.110.131:61619)" duplex="false" />  

<!-- 在group2中 -->  
<networkConnector name="group2-broker1" uri="static:(tcp://192.168.110.128:61616)" duplex="false" />  
<!-- 在group2中 -->  
<networkConnector name="group2-broker2" uri="static:(tcp://192.168.110.129:61617)" duplex="false" />  

上一步配置duplex="true"后,group2中所有的节点只需要配置主从连接,不需要配置静态连接了。ACTIVEMQ_HOME/conf/activemq.xml文件,也不需要再链接group1。

如果在日志中看到类似于如下内容:

Could not start network bridge between: vm://brokerA and: tcp://host3:61618 due to: 拒绝连接 (Connection refused) 

这是由于slave只提供master,拒绝其他一切端口所报的源由,不必惊慌

关于duplex的问题
文中说到了duplex的作用,在最后引出完美方案之前给出了一个集群架构方案,也就是“MASTER SLAVE+BROKER CLUSTER”同时设置duplex为true的方案。
博主给出的解释是,duplex=true之后,才能让消息在不同的broker之间传递。
其实这种理解是错的,broker中的自配置networkConnectors的目的就是要做到在不同的broker之间传递消息,duplex=false时是建立一方向另一方的单向消息,duplex=true时,是建立双向的消息。
比如broker1的tcp连接地址是192.168.0.1:61616,broker2的tcp连接地址是192.168.0.1:61617,如果使用duplex=false建立双向链接,需要在broker1和broker2的两个配置文件中都添加指向对方的networkConnector。如果使用duplex=true,那么只需要在任意一个broker的配置文件中添加即可。

到这里,就已经完成了ActiveMQ高可用+负载均衡的集群搭建,接下来进行配合java测试

测试功能:
1.持久化数据
2.高可用测试
测试方法:
1.生产者连接集群发送50条消息并设置每发送一条消息,sleep1秒
2.观察生产者发送消息所连接的节点,并将所在的节点停掉
3.观察生产者发送消息日志,查看所有消息是不是正常发送

 INFO | Successfully connected to tcp://192.168.110.128:61616
 WARN | Transport (tcp://192.168.110.128:61616) failed , attempting to automatically reconnect: {
    
    }
java.io.EOFException
	at java.io.DataInputStream.readInt(DataInputStream.java:392)
	at org.apache.activemq.openwire.OpenWireFormat.unmarshal(OpenWireFormat.java:268)
	at org.apache.activemq.transport.tcp.TcpTransport.readCommand(TcpTransport.java:240)
	at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:232)
	at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:215)
	at java.lang.Thread.run(Thread.java:748)
 INFO | Successfully reconnected to tcp://192.168.110.131:61619
  **** 消息发送到MQ完成 ****

可以看到现有master宕机之后成功切换到新的高可用.数据也成功消费到并没有丢失.

经测试,当前集群在启动消息生产者发送消息时,使生产者所在节点宕机的情况下,得出如下结论:
1.高可用架构的ActiveMQ集群,在生产消息的过程中生产者所在节点挂掉,客户端会暂时阻塞无法发送消息,但整体可用性不受影响。
2.高可用架构的ActiveMQ集群,在消息生产者所在节点挂掉后,消费者仍可正常消费消息
3.当前ActiveMQ集群若其中一个节点挂掉,ActiveMQ正常提供服务,不影响服务可用性

3.负载均衡测试
最终的架构就是两个master-slave集群相互连通,两个集群可以相互消费对方的消息,但是如果客户端所连接的集群挂掉客户端依然是不能发送消息的,也就是说activemq的负载均衡只是做到消费的负载均衡(消息负载),高可用是靠master-slave来保证的。
测试方法:
1.启动两个消费者监听相同的queue,且服务地址均配置集群所有节点
2.生产者连接集群向指定的queue连续发送20条消息
3.观察两个生产者消费消息的日志
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ZhanBiaoChina/article/details/105840360