木讷大叔爱运维

如何破防Rabbitmq触发内存回收机制

点击上方 蓝色字体 ,关注我们 预计阅读时间3分钟

需求

一般情况下我们使用Zabbix来监控Rabbitmq集群节点的存活状况以及queue队列的消费情况,虽然在一定程度上满足了我们对可用性的监控需求,可一旦节点宕机或进程莫名挂掉,却让我们摸不着头脑。

因为无论是Rabbitmq的Web管理界面以及Zabbix监控都有局限性:

  • Rabbitmq Web管理界面收集的是实时状态,无法有效统计历史数据;
  • Zabbix监控主要面向的是queue队列的消费挤压情况,虽然有历史记录但无法更进一步提供queue的内存使用情况;

针对Rabbitmq节点的异常宕机,经过我们进一步排查,原因基本为rabbitmq进程占用内存过多,触发了Linux系统的内存回收机制,被操作系统OOM杀死进程。因此我们需要进一步了解queue的内存使用情况。

解决方案

我们借助 rabbitmq-top 插件来进一步分析Rabbitmq集群每个节点的queue的使用情况。
  • 按进程 ID、内存使用或减少/秒(CPU 使用的近似度量)排序。
  • 单击进程描述(例如“我的队列”)以查看该对象的管理视图。
  • 单击进程 ID(例如“<0.3423.0>”)以查看更多 Erlang 进程详细信息。

# 安装
# use sudo as necessary
rabbitmq-plugins enable rabbitmq_top
插件安装完成后,我们就可以使用 rabbitmq-top 具体的API,其访问路径如下:
  • /api/top/<节点名称> 进程列表。采用与其他列表、排序、sort_reverse 和列类似的查询字符串参数。排序非常重要,因为它目前硬编码返回前 20 个进程。

  • /api/process/

个别流程细节

因此我们最终的解决方案为

  1. python 访问 /api/top/<节点名称> ,将Top20进程写入到日志文件;
  2. filebeat收集日志,写入到Elasticsearch;
  3. Grafana 接入ES日志源,实现多维度图形化展示;

具体实施

1.python获取Top20进程

# vim rabbitmq_monitor.py
#!/usr/bin/python3
# coding=utf-8

import urllib.request
import urllib.error
import json
import time


log_file = "/tmp/rabbitmq.monitor.log"
user = "admin"
passwd = "Top20"
server = "http://10.10.20.88:15672"
passwdmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
passwdmgr.add_password(None, server, user, passwd)
handler = urllib.request.HTTPBasicAuthHandler(passwdmgr)
opener = urllib.request.build_opener(handler)


def list_nodes():
url = server + '/api/nodes'
request = urllib.request.Request(url)
response = opener.open(request)
data = json.loads(response.read())
nodes = []
for node in data:
name = node['name'].split('@')[1]
element = {'{#NODENAME}': name,
'{#NODETYPE}': node['type']}
nodes.append(element)
return nodes

def get_node_process():
nodes = list_nodes()
proce_data = []
for node in nodes:
node_name = 'rabbit@' + node['{#NODENAME}']
url = server + "/api/top/" + node_name
try:
request = urllib.request.Request(url)
response = opener.open(request)
data = json.loads(response.read())
for proce in data['processes']:
memory = int(proce['memory'])
tmp = {'node':node['{#NODENAME}'],'pid': proce['pid'],'name':proce['name'],'memory': memory}
proce_data.append(tmp)
except:
pass
return proce_data
if __name__ == '__main__':
while True:
f = open(log_file, 'a+')
for data in get_node_process():
json.dump(data,f)
f.write('\n')
f.close()
time.sleep(30)

具体日志如下:

# cat /tmp/rabbitmq.monitor.log
{"node": "node03", "pid": "<0.13714.0>", "name": {"supertype": "queue", "type": "rabbit_mirror_queue_slave", "queue_name": "TEST1.q1", "vhost": "/"}, "memory": 7675288}
{"node": "node03", "pid": "<0.10254.0>", "name": {"supertype": "queue", "type": "gm", "queue_name": "TEST02.q2", "vhost": "/"}, "memory": 163952}
{"node": "node03", "pid": "<0.6129.0>", "name": {"supertype": "queue", "type": "rabbit_mirror_queue_slave", "queue_name": "TEST03.q3", "vhost": "/"}, "memory": 7675336}

2.supervisor 守护

为保证日志的持续输出,我们最好使用supervisor启动rabbitmq_monitor.py 。

# vim /etc/supervisord.d/rabbitmq_monitor.ini
[program:rabbitmq_monitor]
user=work
command=python rabbitmq_monitor.py
numprocs=1
directory=/data/script
autostart=true
autorestart=true
startsecs=10
startretries=3

# 启动
supervisorctl add rabbitmq_monitor
supervisorctl start rabbitmq_monitor

3.filebeat 收集日志到ES



4.Grafana 展示


总结

通过这套方案一旦出现节点宕机的情况,我们只需通过Grafana或ELK查看对应时间段的队列内存占用情况,即可知晓是否有队列过多占用内存,从而触发Linux系统回收机制。 另外我们也可以利用Grafana+Alertmanager接入钉钉、微信等多告警渠道,便于我们更快地发现问题。

1.运维思索系列
2.运维管理系列
3. 运维监控之路
4.蓝鲸之路
5. CI/CD之路
6.Ansible之路

札记:天才就是百分之一的灵感,和百分之九十九的汗水。 但是,这百分之一的灵感远比百分之九十九的汗水重要。 -- 爱迪生 喜欢这篇文章,记得 点赞+在看 哦~