1 概述
rabbitmq作为消息队列,广泛用于生产环境,但是,如果单节点,将导致故障后,生产环境不可用,因此,需要部署高可用环境
本文将介绍基于keepalived+haproxy实现rabbitmq的高可用
rabbitmq的集群中,所有节点都可读写,因此,可用haproxy调度到后端的任意一台rabbitmq上。
环境准备
三个节点mq-01 mq-02 mq-03 ,这里服务器复用了redis的服务器节点,所以机器名忽略。
添加hosts文件
#这一步很关键,所有的节点都要配置一样,否则mq启动会超时,集群功能也不会成功
vim /etc/hosts 192.168.70.32 mq-01 192.168.70.33 mq-02 192.168.70.34 mq-03
另外两台主机用于安装keepalived+haproxy
ip:
192.168.70.35
192.168.70.36
vip:192.168.70.37
2 编译安装erlang
服务包:otp_src_20.2.tar.gz
编译步骤如下
yum -y install make cmake gcc gcc-c++ bison bison-devel ncurses ncurses-devel openssl-devel tar xf otp_src_20.2.tar.gz -C /app cd /app/otp_src_20.2/ ./configure --prefix=/app/erlang && make && make install
#测试erlang是否安装成功
cd /app/erlang/bin/ ./erl
3 编译安装rabbitmq
服务包:rabbitmq-server-generic-unix-3.7.4.tar.xz
tar xf rabbitmq-server-generic-unix-3.7.4.tar.xz -C /app mv /app/rabbitmq_server-3.7.4/ /app/rabbitmq vim /etc/profile export PATH=$PATH:/app/erlang/bin:/app/rabbitmq/sbin source /etc/profile
前台启动,测试启动服务是否报错
./rabbitmq/sbin/rabbitmq-server #前台模式(默认)
后台启动,建议运行服务的方式
/app/rabbitmq/sbin/rabbitmq-server -detached #以后台模式启动(建议),尤其是使用web图形界面时
/app/rabbitmq/sbin/rabbitmq-plugins enable rabbitmq_management #建议安装web图形化管理工具,如不需要 可以不装,15672为rabbitmq服务的图形管理端口
#创建桌面端登陆账号
rabbitmqctl add_vhost vh rabbitmqctl add_user root hns..2018 rabbitmqctl set_user_tags root management rabbitmqctl set_permissions -p vh root ".*" ".*" ".*"
#访问
http://192.168.70.32:15672/
#如果启动不起来 可能是系统自带的erl版本问题 删除/usr/bin/erl 然后source PATH文件即可
4 配置高可用
4.1配置mq高可用
把节点redis-01的.erlang.cookie权限设置为777,并且拷贝到其他两个节点,同时,权限也都要设置为777.erlang.cookie的路径可能不一样,用find命令查找出来
redis-01执行如下命令
chmod 777 /root/.erlang.cookie
删掉02和03的.erlang.cookie
/root/.erlang.cookie
在01上执行
scp /root/.erlang.cookie 192.168.70.33:/root
scp /root/.erlang.cookie 192.168.70.34:/root
拷贝完成后,01,02和03执行如下,恢复原来的权限
chmod 400 /root/.erlang.cookie
确认三台机器的.erlang.cookie值一样
cat /root/.erlang.cookie
启动三个mq节点
rabbitmq-server -detached
停止02和03两个节点app
rabbitmqctl stop_app
在02 和 03上分别执行如下命令
rabbitmqctl join_cluster rabbit@redis-01
rabbitmqctl start_app
此时,如果没有报错,三个节点的mq已经组成rabbit集群
用如下命令进行查看集群状态
rabbitmqctl cluster_status
随便停止某一台rabbitmq,数据都可以从其他两台读取
到这里,rabbitmq集群已经完成,还需配置haproxy+keepalived来实现高可用,只用一个vip来进行调度
4.2 镜像队列的设置
镜像队列的配置通过添加policy完成,policy添加的命令为:
rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]
-p Vhost: 可选参数,针对指定vhost下的queue进行设置
Name: policy的名称
Pattern: queue的匹配模式(正则表达式)
Definition: 镜像定义,包括三个部分 ha-mode,ha-params,ha-sync-mode
ha-mode: 指明镜像队列的模式,有效值为 all/exactly/nodes
all表示在集群所有的节点上进行镜像
exactly表示在指定个数的节点上进行镜像,节点的个数由ha-params指定
nodes表示在指定的节点上进行镜像,节点名称通过ha-params指定
ha-params: ha-mode模式需要用到的参数
ha-sync-mode: 镜像队列中消息的同步方式,有效值为automatic,manually
Priority: 可选参数, policy的优先级
例子:
策略名称 ha-allqueue,对队列名称以app开头的所有队列进行镜像,策略模式为 all 即复制到所有节点,包含新增节点,策略正则表达式为 “^” 表示所有匹配所有队列名称。
policy的设置命令为:
rabbitmqctl set_policy -p vh ha-allqueue "^" '{"ha-mode":"all"}'
策略名称ha-2number,对队列名称以app开头的所有队列进行镜像,并在集群的两个节点上完成镜像,
policy的设置命令为:
rabbitmqctl set_policy -p vh ha-2number "^app" '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
策略名称ha-2node,对队列名称以app开头的所有队列进行镜像,并在集群的rabbit@redis-01和rabbit@redis-02两个指定的节点上完成镜像,
policy的设置命令为:
rabbitmqctl set_policy -p vh ha-2node "^web" '{"ha-mode":"nodes","ha-params":["rabbit@redis-01","rabbit@redis-02"]}'
生产环境可以根据实际情况来配置,第一个情况,虽然匹配了所有,但是当某一个队列不消费,撑爆集群的时候,所有的队列都会被影响,第三个情况指定了所有的 2个队列,只有这两个队列才会拥有web开通的队列镜像信息。
4.3 集群管理相关命令
查看集群状态
rabbitmq-server cluster-status
加入集群
rabbitmqctl join_cluster rabbit@redis-01
查看策略
rabbitmqctl list_policies -p vh
清除策略
rabbitmqctl clear_policy -p vh ha-allweb3.0
5 haproxy+keepalived编译安装
5.1 haproxy编译安装
软件包:haproxy-1.7.9.tar.gz
tar -xf haproxy-1.7.9.tar.gz -C /usr/local groupadd -r -g 159 haproxy useradd -g haproxy -r -s /sbin/nologin -u 159 haproxy cd /usr/local/haproxy-1.7.9/ make TARGET=linux26 ARCH=X86_64 PREFIX=/app/haproxy make install PREFIX=/app/haproxy mkdir /etc/haproxy/ vim /etc/haproxy/haproxy.cfg #放在附录文件 vim /etc/init.d/haproxy #放着附录文件 chmod 777 /etc/init.d/haproxy service haproxy start chkconfig --add haproxy chkconfig --level 2345 haproxy on
5.2 keepalived编译安装
keepalived编译安装见博客:
https://blog.51cto.com/ghbsunny/2154262
5.3 测试
haproxy和keepalived启动后,断开主keepalived,vip会排异到另一台服务器上
另外,通过访问vip 192.168.70.37 的监听5672端口,可以把请求调度到后端的三台mq上,且任何一台mq异常,请求将不会调度到这台异常的mq上,即服务正常。
测试成功
6 附录
6.1 haproxy配置文件
两台配置都一样
vim /etc/haproxy/haproxy.cfg global log 127.0.0.1 local0 info maxconn 8192 user haproxy group haproxy defaults timeout connect 3500 timeout queue 11000 timeout tarpit 12000 timeout client 30000 timeout http-request 40000 timeout http-keep-alive 5000 timeout server 40000 timeout check 7000 option contstats option log-health-checks ################################# ##监控查看本地状态##### listen admin_stats bind 0.0.0.0:9188 mode http log 127.0.0.1 local0 err stats refresh 30s stats uri /haproxy stats realm welcome login\ Haproxy stats auth adminuser:password123456789 stats hide-version stats admin if TRUE #################################### ###反代监控 frontend server bind *:5672 log global mode tcp #option forwardfor default_backend rabbitmq maxconn 3 backend rabbitmq mode tcp log global balance roundrobin server mq-01 192.168.70.32:5672 check inter 5s rise 2 fall 3 #check inter 2000 是检测心跳频率,rise 2是2次正确认为服务器可用,fall 3是3次失败认为服务器不可用 server mq-02 192.168.70.33:5672 check inter 5s rise 2 fall 3 server mq-03 192.168.70.34:5672 check inter 5s rise 2 fall 3 #以上为完整的haproxy配置,如果在rabbitmq的消息消费端出现错误: o.s.a.r.c.CachingConnectionFactory : Channel shutdown: connection error,但是请求可以被正常消费, 解决办法是:修改 HAProxy 中的timeout client超时时间,配置大于系统的tcp_keepalive_time间隔时间(推荐)即设置timeout client和 timeout server值 参考文章;配置如下: frontend mqctl bind *:15672 log global mode tcp #option forwardfor default_backend mqweb maxconn 3 timeout client 3h backend mqweb mode tcp log global balance roundrobin timeout server 3h server prd-can3-0-mq01 192.168.70.71:15672 check inter 5s rise 2 fall 3 #check inter 2000 是检测心跳频率,rise 2是2次正确认为服务器可用,fall 3是3次失败认为服务器不可用 server prd-can3-0-mq02 192.168.70.81:15672 check inter 5s rise 2 fall 3
6.2 haproxy启动文件
#编译安装的执行文件的路径需要调整,其他地方可保持一致
vim /etc/init.d/haproxy #!/bin/sh # # haproxy # # chkconfig: - 85 15 # description: HAProxy is a free, very fast and reliable solution \ # offering high availability, load balancing, and \ # proxying for TCP and HTTP-based applications # processname: haproxy # config: /etc/haproxy/haproxy.cfg # pidfile: /var/run/haproxy.pid # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ "$NETWORKING" = "no" ] && exit 0 exec="/app/haproxy/sbin/haproxy" #这里要注意,编译安装的执行文件的路径 prog=$(basename $exec) [ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog cfgfile=/etc/haproxy/haproxy.cfg pidfile=/var/run/haproxy.pid lockfile=/var/lock/subsys/haproxy check() { $exec -c -V -f $cfgfile $OPTIONS } start() { $exec -c -q -f $cfgfile $OPTIONS if [ $? -ne 0 ]; then echo "Errors in configuration file, check with $prog check." return 1 fi echo -n $"Starting $prog: " # start it up here, usually something like "daemon $exec" daemon $exec -D -f $cfgfile -p $pidfile $OPTIONS retval=$? echo [ $retval -eq 0 ] && touch $lockfile return $retval } stop() { echo -n $"Stopping $prog: " # stop it here, often "killproc $prog" killproc $prog retval=$? echo [ $retval -eq 0 ] && rm -f $lockfile return $retval } restart() { $exec -c -q -f $cfgfile $OPTIONS if [ $? -ne 0 ]; then echo "Errors in configuration file, check with $prog check." return 1 fi stop start } reload() { $exec -c -q -f $cfgfile $OPTIONS if [ $? -ne 0 ]; then echo "Errors in configuration file, check with $prog check." return 1 fi echo -n $"Reloading $prog: " $exec -D -f $cfgfile -p $pidfile $OPTIONS -sf $(cat $pidfile) retval=$? echo return $retval } force_reload() { restart } fdr_status() { status $prog } case "$1" in start|stop|restart|reload) $1 ;; force-reload) force_reload ;; check) check ;; status) fdr_status ;; condrestart|try-restart) [ ! -f $lockfile ] || restart ;; *) echo $"Usage: $0 {start|stop|status|restart|try-restart|reload|force-reload}" exit 2 esac
6.3 keepalived配置文件
主备的配置文件有三点不同
router_id不一样
优先级不一样
state主从不一样
其他都一样
vim /etc/keepalived/keepalived.conf #Keepalived配置文件 global_defs { router_id NodeA #路由ID, 主备的ID不能相同 } vrrp_instance VI_1 { state MASTER #Keepalived的角色。Master表示主服务器,从服务器设置为BACKUP interface eth0 #指定监测网卡 virtual_router_id 35 priority 100 #优先级,BACKUP机器上的优先级要小于这个值 advert_int 1 #设置主备之间的检查时间,单位为s authentication { #定义验证类型和密码 auth_type PASS auth_pass password123456789 } virtual_ipaddress { #VIP地址,可以设置多个: 192.168.70.35 } }