rabbitmq集群第三篇--优化之我见

rabbitmq之优化畅谈

在本文中主要讲一下RabbitMQ集群的一些优化,因为近期公司业务系统因为连接不上MQ集群出现了很多问题,本文主要总结一些解决的方案:

1. RabbitMQ集群的架构:

  我们公司的RabbitMQ集群架构是我设计和实施的,当时也是参考了很多互联网上的架构文档;2台LVS服务器,3台MQ服务器。2台是内存节点,一台是磁盘节点;LVS服务器又通过keepalived实现了主从。VIP地址在主服务器上面,LVS负责分发客户端请求,通过Round-Robin轮训策略来负载。负载均衡器可以是LVS,也可以是haproxy.刚开始实施的时候我用的是Haproxy来实现负载均衡。后面因为Haproxy不支持来源Ip显示,每次在MQ管理页面排查问题都不知道是哪个客户端连接过来的,所以后面改成了LVS。

rabbitmq集群第三篇--优化之我见_第1张图片

2. RabbitMQ集群的优化:

1. 修改heartbeat心跳超时时间:

  heartbeat通常用来检测通信的对端是否存活(未正常关闭socket连接而异常crash)。其基本原理是检测对应的socket连接上数据的收发是否正常,如果一段时间内没有收发数据,则向对端发送一个心跳检测包,如果一段时间内没有回应则认为心跳超时,即认为对端可能异常crash了。rabbitmq也不例外,heatbeat在客户端和服务端之间用于检测对端是否正常,即客户端与服务端之间的tcp链接是否正常。

1. 服务端的设置:

  heartbeat通常用来检测通信的对端是否存活(未正常关闭socket连接而异常crash)。其基本原理是检测对应的socket连接上数据的收发是否正常,如果一段时间内没有收发数据,则向对端发送一个心跳检测包,如果一段时间内没有回应则认为心跳超时,即认为对端可能异常crash了。rabbitmq也不例外,heatbeat在客户端和服务端之间用于检测对端是否正常,即客户端与服务端之间的tcp链接是否正常。
  heartbeat检测时间间隔可在配置文件rabbitmq.config中增加配置项{heartbeat,Timeout}进行配置,其中Timeout指定时间间隔,单位为秒.

[
{rabbit, [{tcp_listeners, [56720]}, {heartbeat,300}, {loopback_users, []}, {vm_memory_high_watermark, 0.6}]},
{rabbitmq_management, [{listener, [{port, 56721}]},{loopback_users, []}]},
{rabbitmq_tracing, [{username, "guest"}, {password, "guest111"}]}
].
2. JAVA客户端的设置:
ConnectionFactory cf = new ConnectionFactory();
// set the heartbeat timeout to 60 seconds
cf.setRequestedHeartbeat(60);

默认的心跳时间为60s,服务器端默认配置就是60s,也就是不配置也是60s.生产者或者消费者应用配置了其他心跳时间仍然不生效,因为协商的时候是以最小的配置为准。所以需要将服务器的心跳超时时间也改成300s才可以;

2. 配置rabbitmq的日志跟踪:

  有时候我们在连接rabbitmq的时候,会出现一些问题;比如生产者发送了消息到mq集群,但是消费者没有收到消息。然后生产者项目组说自己的配置没有问题,消费者也说自己的项目配置没有问题。这个时候就需要将rabbitmq的日志跟踪打开,实时查看具体的日志发送内容,排查业务问题;因为默认rabbitmq的应用日志非常简单,只有简单的连接IP,连接那个vhost信息等;开启的方式如下:

rabbitmq-plugins enable rabbitmq_tracing

通过rabbitmq命令行界面开启日志跟踪插件,然后通过web管理控制台针对单独的队列配置监控

rabbitmq集群第三篇--优化之我见_第2张图片

需要配置要监控的vhost,里面一定要有guest账户的权限

rabbitmq集群第三篇--优化之我见_第3张图片

选择tracing页面,配置要监控的vhost,输入监控的名称,选择JSON格式,选择要监控的队列,可以使用#.队列名的方式;

rabbitmq集群第三篇--优化之我见_第4张图片

3. MQ消息的迁移:

   我们在使用rabbitmq的时候,有这种场景。因为我们有多数据中心,多个数据中心分别在不同的城市。为了减少专线的网络延迟,我们在每一个数据中心部署了一套MQ集群。有些业务因为某些原因,需要从A数据中心迁移到B数据中心,我需要在B数据中心创建MQ的账户和Vhost,迁移的时候如果队列里面还有未消费的消息,还需要将这些消息一同迁移过来。怎么迁移消息了,还是需要用到rabbitmq的插件;

# 首先在来源的MQ集群开启插件
rabbitmq-plugins enable rabbitmq_shovel
rabbitmq-plugins enable rabbitmq_shovel_management
rabbitmq-plugins enable rabbitmq_federation
rabbitmq-plugins enable rabbitmq_federation_management

rabbitmq集群第三篇--优化之我见_第5张图片

安装好rabbitmq_shovel和rabbitmq_shovel_management两个插件之后,就可以在rabbitmq的管理页面查看到这两个插件,首先创建一个shovel.填入的信息主要包括要同步的vhost名称,起一个名字,来源mq服务器地址和队列,目标mq服务器地址和队列,其中要有对vhost授权的账户和密码。目标MQ集群要提前创建好vhost,配置的授权账户和密码,包括队列等。

MQ消息迁移2

还有一个插件Federation,这个插件一般用于实时同步消息的。我看官网上描述主要用于广域网上面的两个不同的MQ集群实时同步消息,我们公司用不到这种场景,所以没怎么深究;

4. MQ配置的备份和还原:

  MQ的备份可以由多种方式,可以通过web管理页面备份一个vhost的信息(包括队列、EXCHANGE、Bingding Key等),备份vhost一般在目标MQ集群要提前创建好用户、vhost、授权等,才可以恢复单独的vhost配置;也可以备份整个MQ的配置信息,用于还原一个空的MQ集群;如果目标MQ集群里面已经有配置,是追加而不是覆盖的方式;

rabbitmq集群第三篇--优化之我见_第6张图片

导出一个vhost的配置,也可以选择all所有的配置导出

rabbitmq集群第三篇--优化之我见_第7张图片

导入配置到另外一个vhost里面,也可以选择导入所有的配置,所有的配置导入建议在空的mq集群执行;不然容易造成配置冲突;

  但是这个是需要手工操作的,我们备份MQ的配置信息应该是每天定时备份才行,因为你也不知道哪一天MQ集群会出现故障;我写了一个shell脚本来实现每天定时备份MQ集群的配置信息。脚本的主要用途就是每天备份MQ的配置,并且备份完成之后会发邮件给我,告知我备份是否成功;脚本的内容如下:

#!/bin/bash
#
RABBITMQ_HOME=/usr/local/rabbitmq_server-3.6.9
PATH=$PATH:$RABBITMQ_HOME/sbin
export RABBITMQ_HOME PATH
ERLANG_HOME=/usr/local/erlang
PATH=$PATH:$ERLANG_HOME/bin
export ERLANG_HOME PATH
RABBITMQADMIN_HOME=/sbin
PATH=$PATH:$RABBITMQADMIN_HOME
export RABBITMQADMIN_HOME PATH

env_ip() {
array2=(
192.168.20.232
192.168.22.94
192.168.0.242
192.168.1.251
192.168.0.21
192.168.0.210
192.168.74.200
)
if [ $(ifconfig |grep eno16777984|wc -l) -eq 1 ];then
    mq_server_ip=${array2[1]}
    env_name=uat
    env="小小金融UAT环境MQ集群"
elif [ $(ifconfig eth1|awk 'NR==2 {print $2}'|awk -F ":" '{print $2}'|grep 192.168.20|wc -l) -eq 1 ];then
    mq_server_ip=${array2[0]}
    env_name=sit
    env="小小金融SIT环境MQ集群"
elif [ $(ifconfig eth1|awk 'NR==2 {print $2}'|awk -F ":" '{print $2}'|grep 192.168.0|wc -l) -eq 1 ];then
    mq_server_ip=${array2[2]}
    env_name=WHPRD01
    env="小小金融武汉PRD环境第一套MQ集群"
elif [ $(ifconfig eth1|awk 'NR==2 {print $2}'|awk -F ":" '{print $2}'|grep 192.168.1|wc -l) -eq 1 ];then
    mq_server_ip=${array2[3]}
    env_name=WHPRD02
    env="小小金融武汉PRD环境第二套MQ集群"
elif [ $(ifconfig eth1|awk 'NR==2 {print $2}'|awk -F ":" '{print $2}'|grep 192.168.0|egrep -v '[0-9][0-9][0-9]'|wc -l) -eq 1 ];then
    mq_server_ip=${array2[4]}
    env_name=XXPRD
    env="小小金融深圳PRD环境MQ集群"
elif [ $(ifconfig eth1|awk 'NR==2 {print $2}'|awk -F ":" '{print $2}'|grep 192.168.0|egrep  '[0-9][0-9][0-9]'|wc -l) -eq 1 ];then
    mq_server_ip=${array2[5]}
    env_name=XXTTPRD
    env="小小金融深圳小小独立PRD环境MQ集群"
elif [ $(ifconfig -a|grep Bcast|awk -F "[ :]+" '{print $4}'|grep 192.168.74|wc -l) -eq 1 ];then
    mq_server_ip=${array2[6]}
    env_name=XXTTUAT
    env="小小金融深圳小小独立UAT环境MQ集群"

fi
}
env_ip
mqadmin_user="admin"
mqadmin_pw="admin"
mqadmin_port="56721"
mq_config_backup_file="/data/mq_config_bak/${mq_server_ip}_mqconfigbak_$(date +%Y%m%d%H%M%S)"
if [ ! -d /data/mq_config_bak ];then
mkdir -p /data/mq_config_bak
fi
if [ "`ls -A /data/mq_config_bak`" != "" ];then
   rm -rf /data/mq_config_bak/*
fi

/sbin/rabbitmqadmin -H ${mq_server_ip} -P ${mqadmin_port} -u ${mqadmin_user} -p ${mqadmin_pw} export ${mq_config_backup_file} > /dev/null 2>&1
if [ "`ls -A /data/mq_config_bak`" != "" ];then
   scp -P 10022 -r /data/mq_config_bak/* [email protected]:/data/mq_config_bak/ &>/dev/null
   ssh -p 10022 [email protected] /bin/sh /.scripts/send_mqconfigbak.sh ${mq_server_ip} ${env}  &>/dev/null

fi

rabbitmqadmin使用api调用的工具,在mq的管理页面就可以直接下载;

5. 定时清理测试环境的MQ消息:

  这个脚本只能在测试环境运行,千万不要在生产环境运行。因为测试MQ集群经常会有消息堆积,并且测试环境的MQ集群配置比较低,经常会因为消息堆积而导致内存爆满,导致MQ服务器故障。因为MQ的消息没有消费的话都是存在于内存中;而生产环境的话,一般不能轻易清除消息。要和研发人员确认才行;

#!/bin/bash
#
echo > /tmp/queues.txt
echo > /tmp/queues2.txt
VHOST2=$(rabbitmqctl list_vhosts|grep -v "Listing vhosts")
for i in ${VHOST2[@]}
        do
          rabbitmqctl list_queues -p $i|grep -v "Listing queues"|awk '{$1="'${i}' "$1;print}' >> /tmp/queues.txt
        done
cat /tmp/queues.txt|sort -rn -k3|sed '/^$/d' > /tmp/queues2.txt
file2="/tmp/queues2.txt"
cat $file2|while read line
    do
             queues_number=$(echo $line|awk '{print $NF}')
         if [ $queues_number -gt 5000 ];then
             queues_name=$(echo $line|awk '{print $2}')
             vhost_name=$(echo $line|awk '{print $1}')
             echo "$(date +%F-%H:%M:%S) the vhost ${vhost_name} include the queues ${queues_name} message number is ${queues_number} ,Because the number of messages exceeds 5000, the messages will be cleared" >> /tmp/purge_message.txt
             /usr/local/rabbitmq_server-3.6.9/sbin/rabbitmqctl purge_queue -p ${vhost_name} ${queues_name} >/dev/null 2>&1
         fi
    done

此脚本的主要用途就是当测试环境MQ集群的队列里面的消息数量超过5000,就会清理掉这些消息,并且写日志到固定文件中;然后把这个脚本放在linux的crontab里面每一个小时执行一次;

6. MQ客户端代码层的一些优化:

   前面讲到了MQ的一些优化,主要针对网络层的和架构层的。因为我的工作就是负责MQ服务器本身的稳定;关于研发代码层的优化包括如下:

  • 队列、消息都需要持久化;
  • 网络延迟是需要配置心跳的超时时间;
  • 需要有重连机制;
  • 生产者和消费者都需要有确认机制,比如配置ack等;
  • 重要队列还需要配置死信队列、镜像队列等;

7. 提供一个单机版MQ安装脚本:

#!/bin/bash
#
yum  -y install make gcc gcc-c++ kernel-devel m4 unixODBC unixODBC-devel openssl openssl-deetc
yum  -y install ncurses-devel
XXet -O /software/otp_src_19.0.tar.gz ftp://xxjrftp:Pass123$%^@192.168.20.27:9020/software/rabbitmq/otp_src_19.0.tar.gz
XXet -O /software/wxWidgets-3.0.2.tar.bz2 ftp://xxjrftp:Pass123$%^@192.168.20.27:9020/software/rabbitmq/wxWidgets-3.0.2.tar.bz2
XXet -O /software/rabbitmq-server-generic-unix-3.6.9.tar ftp://xxjrftp:Pass123$%^@192.168.20.27:9020/software/rabbitmq/rabbitmq-server-generic-unix-3.6.9.tar
cd /software
tar xzvf otp_src_19.0.tar.gz
cd otp_src_19.0
./configure --prefix=/usr/local/erlang --without-javac
value=$(echo $?)
if [ ${value} -eq 0 ];then
    make && make install
fi
echo -e "ERLANG_HOME=/usr/local/erlang\nPATH=\$PATH:\$ERLANG_HOME/bin\nexport ERLANG_HOME PATH\n" >>/etc/profile.d/erlang.sh
source /etc/profile.d/erlang.sh
yum -y install gtk2-devel binuti-devel mesa-libGL-devel mesa-libGLU-devel
cd /software && tar jxvf wxWidgets-3.0.2.tar.bz2  && cd wxWidgets-3.0.2
./configure --with-opengl --enable-debug --enable-unicode
value=$(echo $?)
if [ ${value} -eq 0 ];then
    make && make install
fi
cd /software
tar xf rabbitmq-server-generic-unix-3.6.9.tar
mv /software/rabbitmq_server-3.6.9 /usr/local
cd /usr/local
ln -sv rabbitmq_server-3.6.9   rabbitmq_server
echo -e "RABBITMQ_HOME=/usr/local/rabbitmq_server\nPATH=\$PATH:\$RABBITMQ_HOME/sbin\nexport RABBITMQ_HOME PATH\n" >>/etc/profile.d/rabbitmq.sh
source /etc/profile.d/rabbitmq.sh
XXet -O /etc/init.d/rabbitmq-server ftp://xxjrftp:Pass123$%^@192.168.20.27:9020/software/rabbitmq/rabbitmq-server
chmod +x /etc/init.d/rabbitmq-server
cd /etc/init.d
chkconfig --add rabbitmq-server
chkconfig rabbitmq-server on
mkdir -p /logs/rabbitmq
mkdir -p /var/run/rabbitmq
ln -s /usr/local/erlang/bin/erl /usr/bin/erl
XXet -O /usr/local/rabbitmq_server-3.6.9/etc/rabbitmq/enabled_plugins ftp://xxjrftp:Pass123$%^@192.168.20.27:9020/software/rabbitmq/enabled_plugins
XXet -O /usr/local/rabbitmq_server-3.6.9/etc/rabbitmq/rabbitmq.config ftp://xxjrftp:Pass123$%^@192.168.20.27:9020/software/rabbitmq/rabbitmq.config
service rabbitmq-server start
sleep 15
source /etc/profile
rabbitmqctl add_user admin 11111
rabbitmqctl set_user_tags admin administrator
rabbitmqctl change_password guest 222222
systemctl stop iptables
systemctl disable iptables

博文的更详细内容请关注我的个人微信公众号 “云时代IT运维”,本公众号旨在共享互联网运维新技术,新趋势; 包括IT运维行业的咨询,运维技术文档分享。重点关注devops、jenkins、zabbix监控、kubernetes、ELK、各种中间件的使用,比如redis、MQ等;shell和python等运维编程语言;本人从事IT运维相关的工作有十多年。2008年开始专职从事Linux/Unix系统运维工作;对运维相关技术有一定程度的理解。本公众号所有博文均是我的实际工作经验总结,基本都是原创博文。我很乐意将我积累的经验、心得、技术与大家分享交流!希望和大家在IT运维职业道路上一起成长和进步;

你可能感兴趣的:(rabbitmq集群第三篇--优化之我见)