文档章节

【原创】RabbitMQ 之 file descriptor limit alarm 分析

摩云飞
 摩云飞
发布于 2016/05/20 13:46
字数 1416
阅读 825
收藏 1
点赞 3
评论 5

【告警信息分析】

在 fd 超限后首先会在 RabbitMQ 日志中看到类似下面的信息

...
=INFO REPORT==== 25-Sep-2015::10:35:48 ===
accepting AMQP connection <0.27563.2> (172.16.185.147:49571 -> 172.16.185.147:6672)

=WARNING REPORT==== 25-Sep-2015::10:35:48 ===
file descriptor limit alarm set.

********************************************************************
*** New connections will not be accepted until this alarm clears ***
********************************************************************

=INFO REPORT==== 25-Sep-2015::10:35:48 ===
accepting AMQP connection <0.27566.2> (172.16.185.147:49572 -> 172.16.185.147:6672)

=WARNING REPORT==== 25-Sep-2015::10:35:48 ===
closing AMQP connection <0.27566.2> (172.16.185.147:49572 -> 172.16.185.147:6672):
connection_closed_abruptly

=WARNING REPORT==== 25-Sep-2015::10:35:48 ===
closing AMQP connection <0.27563.2> (172.16.185.147:49571 -> 172.16.185.147:6672):
connection_closed_abruptly

=WARNING REPORT==== 25-Sep-2015::10:35:48 ===
file descriptor limit alarm cleared

=INFO REPORT==== 25-Sep-2015::10:35:48 ===
accepting AMQP connection <0.27569.2> (172.16.185.147:51510 -> 172.16.185.147:6672)

=WARNING REPORT==== 25-Sep-2015::10:35:48 ===
file descriptor limit alarm set.

********************************************************************
*** New connections will not be accepted until this alarm clears ***
********************************************************************

=WARNING REPORT==== 25-Sep-2015::10:35:48 ===
closing AMQP connection <0.27569.2> (172.16.185.147:51510 -> 172.16.185.147:6672):
connection_closed_abruptly

...

从上面的告警信息可以看出处理逻辑如下:

  • accept 一条新的 AMQP connection
  • 触发 fd limit 告警
  • 当再 accept 一条新 AMQP connection 时直接 connection_closed_abruptly
  • 当再关闭一条 AMQP connection 后 fd limit 告警解除
  • 重复 1-4 步骤


【可用 socket 限制值计算】

在 RabbitMQ 服务刚启动时,会在相应日志中记录如下内容

=INFO REPORT==== 14-Jul-2015::04:28:19 ===
Limiting to approx 924 file handles (829 sockets)

该内容表明了当前 RabbitMQ 服务的 fd 使用限制值;

在 file_handle_cache.erl 中

%% 指定告警触发和清除函数
start_link() ->
    start_link(fun alarm_handler:set_alarm/1, fun alarm_handler:clear_alarm/1).

start_link(AlarmSet, AlarmClear) ->
    gen_server2:start_link({local, ?SERVER}, ?MODULE, [AlarmSet, AlarmClear],
                           [{timeout, infinity}]).
...
init([AlarmSet, AlarmClear]) ->
    Limit = case application:get_env(file_handles_high_watermark) of   %% 该环境变量默认是不设置的
                {ok, Watermark} when (is_integer(Watermark) andalso
                                      Watermark > 0) ->
                    Watermark;
                _ ->
                    case ulimit() of   %% 获取系统设置
                        unknown  -> ?FILE_HANDLES_LIMIT_OTHER;  %% 默认值 1024
                        Lim      -> lists:max([2, Lim - ?RESERVED_FOR_OTHERS])  %% 需要预留 100 个 fd 给系统使用
                    end
            end,
    ObtainLimit = obtain_limit(Limit),
    error_logger:info_msg("Limiting to approx ~p file handles (~p sockets)~n",
                          [Limit, ObtainLimit]),
    Clients = ets:new(?CLIENT_ETS_TABLE, [set, private, {keypos, #cstate.pid}]),
    Elders = ets:new(?ELDERS_ETS_TABLE, [set, private]),
    {ok, #fhc_state { elders                = Elders,
                      limit                 = Limit,              %% fd 数目总体限制
                      open_count            = 0,
                      open_pending          = pending_new(),
                      obtain_limit          = ObtainLimit,        %% 用于 socket 的 fd 限制
                      obtain_count_file     = 0,
                      obtain_pending_file   = pending_new(),
                      obtain_count_socket   = 0,
                      obtain_pending_socket = pending_new(),
                      clients               = Clients,
                      timer_ref             = undefined,
                      alarm_set             = AlarmSet,
                      alarm_clear           = AlarmClear }}.
...

-define(OBTAIN_LIMIT(LIMIT), trunc((LIMIT * 0.9) - 2)).    %% 这里进行了值修正
...
obtain_limit(infinity) -> infinity;
obtain_limit(Limit)    -> case ?OBTAIN_LIMIT(Limit) of
                              OLimit when OLimit < 0 -> 0;
                              OLimit                 -> OLimit
                          end.
...
%% To increase the number of file descriptors: on Windows set ERL_MAX_PORTS
%% environment variable, on Linux set `ulimit -n`.
ulimit() ->
    case proplists:get_value(max_fds, erlang:system_info(check_io)) of
        MaxFds when is_integer(MaxFds) andalso MaxFds > 1 ->
            case os:type() of
                {win32, _OsName} ->
                    %% On Windows max_fds is twice the number of open files:
                    %%   https://github.com/yrashk/erlang/blob/e1282325ed75e52a98d5/erts/emulator/sys/win32/sys.c#L2459-2466
                    MaxFds div 2;
                _Any ->
                    %% For other operating systems trust Erlang.
                    MaxFds
            end;
        _ ->
            unknown
    end.

所以日志中的输出值是按如下公式计算得到的

=INFO REPORT==== 14-Jul-2015::04:28:19 ===
Limiting to approx 924 file handles (829 sockets)

ulimit -n 为 1024
924 = 1024 - 100
829 = trunc((1024 - 100) * 0.9 - 2)

【socket 限制告警的触发和解除】

在 file_handle_cache.erl 中

adjust_alarm(OldState = #fhc_state { alarm_set   = AlarmSet,
                                     alarm_clear = AlarmClear }, NewState) ->
    case {obtain_limit_reached(OldState), obtain_limit_reached(NewState)} of
        {false, true} -> AlarmSet({file_descriptor_limit, []});   %% 触发 socket 超限告警
        {true, false} -> AlarmClear(file_descriptor_limit);   %% 清除 socket 超限告警
        _             -> ok
    end,
    NewState.

其中
AlarmSet 对应 fun alarm_handler:set_alarm/1
AlarmClear 对应 fun alarm_handler:clear_alarm/1

在 rabbit_alarm.erl 中

...
set_alarm(Alarm)   -> gen_event:notify(?SERVER, {set_alarm,   Alarm}).
clear_alarm(Alarm) -> gen_event:notify(?SERVER, {clear_alarm, Alarm}).
...
handle_event({set_alarm, Alarm}, State = #alarms{alarms = Alarms}) ->
    case lists:member(Alarm, Alarms) of
        true  -> {ok, State};
        false -> UpdatedAlarms = lists:usort([Alarm|Alarms]),
                 handle_set_alarm(Alarm, State#alarms{alarms = UpdatedAlarms})
    end;

handle_event({clear_alarm, Alarm}, State = #alarms{alarms = Alarms}) ->
    case lists:keymember(Alarm, 1, Alarms) of
        true  -> handle_clear_alarm(
                   Alarm, State#alarms{alarms = lists:keydelete(
                                                  Alarm, 1, Alarms)});
        false -> {ok, State}

    end;
...
%% 触发告警
handle_set_alarm({file_descriptor_limit, []}, State) ->
    rabbit_log:warning(
      "file descriptor limit alarm set.~n~n"
      "********************************************************************~n"
      "*** New connections will not be accepted until this alarm clears ***~n"
      "********************************************************************~n"),
    {ok, State};
...
%% 清除告警
handle_clear_alarm(file_descriptor_limit, State) ->
    rabbit_log:warning("file descriptor limit alarm cleared~n"),
    {ok, State};
...

【rabbitmqctl 子命令 status】

通过 rabbitmqctl 的 status 子命令也可以看到当前 fd 使用情况;

[root@Betty ~]# rabbitmqctl status
Status of node rmq_betty@Betty ...
[{pid,20008},
...
 {file_descriptors,
     [{total_limit,924},{total_used,13},{sockets_limit,829},{sockets_used,8}]},
...
...done.
[root@Betty ~]#

在 rabbit_control_main.erl 中

...
action(status, Node, [], _Opts, Inform) ->
    Inform("Status of node ~p", [Node]),
    display_call_result(Node, {rabbit, status, []});
...

在 rabbit.erl 中

status() ->
...
    S3 = rabbit_misc:with_exit_handler(
           fun () -> [] end,
           fun () -> [{file_descriptors, file_handle_cache:info()}] end),
...

在 file_handle_cache.erl 中

%%----------------------------------------------------------------------------
-define(INFO_KEYS, [total_limit, total_used, sockets_limit, sockets_used]).
...
info() -> info(?INFO_KEYS).
info(Items) -> gen_server2:call(?SERVER, {info, Items}, infinity).
...
handle_call({info, Items}, _From, State) ->
    {reply, infos(Items, State), State}.
...
infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items].

i(total_limit,   #fhc_state{limit               = Limit}) -> Limit;
i(total_used,    State)                                   -> used(State);
i(sockets_limit, #fhc_state{obtain_limit        = Limit}) -> Limit;
i(sockets_used,  #fhc_state{obtain_count_socket = Count}) -> Count;
i(Item, _) -> throw({bad_argument, Item}).

used(#fhc_state{open_count          = C1,
                 obtain_count_socket = C2,
                 obtain_count_file   = C3}) -> C1 + C2 + C3.

【限制调整(最简方式)】

[root@Betty ~]# ps aux|grep rabbit
root     20008  0.6  1.7 2291544 66796 ?       Sl   May18  20:53 /usr/local/lib/erlang/erts-6.0/bin/beam.smp -W w -K true -A30 -P 1048576 -- -root /usr/local/lib/erlang -progname erl -- -home /root -- -pa /usr/lib/rabbitmq/sbin/../ebin -noshell -noinput -s rabbit boot -sname rmq_betty -boot start_sasl -config /etc/rabbitmq/rabbitmq -kernel inet_default_connect_options [{nodelay,true}] -sasl errlog_type error -sasl sasl_error_logger false -rabbit error_logger {file,"/var/log/rabbitmq/rmq_betty.log"} -rabbit sasl_error_logger {file,"/var/log/rabbitmq/rmq_betty-sasl.log"} -rabbit enabled_plugins_file "/etc/rabbitmq/enabled_plugins" -rabbit plugins_dir "/usr/lib/rabbitmq/sbin/../plugins" -rabbit plugins_expand_dir "/var/lib/rabbitmq/mnesia/rmq_betty-plugins-expand" -os_mon start_cpu_sup false -os_mon start_disksup false -os_mon start_memsup false -mnesia dir "/var/lib/rabbitmq/mnesia/rmq_betty" -kernel inet_dist_listen_min 25672 -kernel inet_dist_listen_max 25672 -noshell -noinput
root     32060  0.0  0.0 103252   852 pts/1    S+   13:22   0:00 grep rabbit
[root@Betty ~]# 
[root@Betty ~]# cat /proc/20008/limits | grep "open"
Max open files            1024                 4096                 files     
[root@Betty ~]#
[root@Betty ~]# ulimit -n
1024
[root@Betty ~]# 
[root@Betty ~]# ulimit -n 10240
[root@Betty ~]# ulimit -n
10240
[root@Betty ~]# 
[root@Betty ~]# rabbitmqctl stop
Stopping and halting node rmq_betty@Betty ...
Args = []
 ...
...done.
[root@Betty ~]#
[root@Betty ~]# rabbitmq-server -detached
Warning: PID file not written; -detached was passed.
[root@Betty ~]# 
[root@Betty ~]# ps aux|grep rabbit
root     32202 33.5  1.4 2296228 56552 ?       Sl   13:24   0:02 /usr/local/lib/erlang/erts-6.0/bin/beam.smp -W w -K true -A30 -P 1048576 -- -root /usr/local/lib/erlang -progname erl -- -home /root -- -pa /usr/lib/rabbitmq/sbin/../ebin -noshell -noinput -s rabbit boot -sname rmq_betty -boot start_sasl -config /etc/rabbitmq/rabbitmq -kernel inet_default_connect_options [{nodelay,true}] -sasl errlog_type error -sasl sasl_error_logger false -rabbit error_logger {file,"/var/log/rabbitmq/rmq_betty.log"} -rabbit sasl_error_logger {file,"/var/log/rabbitmq/rmq_betty-sasl.log"} -rabbit enabled_plugins_file "/etc/rabbitmq/enabled_plugins" -rabbit plugins_dir "/usr/lib/rabbitmq/sbin/../plugins" -rabbit plugins_expand_dir "/var/lib/rabbitmq/mnesia/rmq_betty-plugins-expand" -os_mon start_cpu_sup false -os_mon start_disksup false -os_mon start_memsup false -mnesia dir "/var/lib/rabbitmq/mnesia/rmq_betty" -kernel inet_dist_listen_min 25672 -kernel inet_dist_listen_max 25672 -noshell -noinput
root     32250  0.0  0.0 103252   852 pts/1    S+   13:24   0:00 grep rabbit
[root@Betty ~]# 
[root@Betty ~]# cat /proc/32202/limits | grep "open"
Max open files            10240                10240                files 
[root@Betty ~]# rabbitmqctl status
Status of node rmq_betty@Betty ...
[{pid,32202},
...
 {file_descriptors,
     [{total_limit,10140},
      {total_used,12},
      {sockets_limit,9124},
      {sockets_used,7}]},
...
...done.
[root@Betty ~]#

 



 

© 著作权归作者所有

共有 人打赏支持
摩云飞
粉丝 364
博文 352
码字总数 952690
作品 0
徐汇
程序员
加载中

评论(5)

开源中国撞死人
开源中国撞死人

引用来自“ywjay”的评论

楼主.想问下rabbitmq能不能实现在消费者那里做处理完后进行通知生产者.使用ack的话好像不太好实现.因为如果没消费者的时候也会返回ack..我的是3.6.2版本的.好像已经没有immediate这个属性.

引用来自“摩云飞”的评论

immediate 属性被去掉后,Producer 已经没有办法直接获取到 Consumer 是否 ack 了相应消息的通知了,只能通过组合使用 rabbitmq 的方案,实现类似的功能~

引用来自“ywjay”的评论

好的.明白了.谢谢楼主.

引用来自“摩云飞”的评论

0
楼主.我还有最后一个问题哈.就是我在生产端调用waitForConfirmsOrDie好像不会进行阻塞等待返回ack啊.我用的版本是3.6.2...难道他和immediate属性一样不支持了吗.
摩云飞
摩云飞

引用来自“ywjay”的评论

楼主.想问下rabbitmq能不能实现在消费者那里做处理完后进行通知生产者.使用ack的话好像不太好实现.因为如果没消费者的时候也会返回ack..我的是3.6.2版本的.好像已经没有immediate这个属性.

引用来自“摩云飞”的评论

immediate 属性被去掉后,Producer 已经没有办法直接获取到 Consumer 是否 ack 了相应消息的通知了,只能通过组合使用 rabbitmq 的方案,实现类似的功能~

引用来自“ywjay”的评论

好的.明白了.谢谢楼主.
0
开源中国撞死人
开源中国撞死人

引用来自“ywjay”的评论

楼主.想问下rabbitmq能不能实现在消费者那里做处理完后进行通知生产者.使用ack的话好像不太好实现.因为如果没消费者的时候也会返回ack..我的是3.6.2版本的.好像已经没有immediate这个属性.

引用来自“摩云飞”的评论

immediate 属性被去掉后,Producer 已经没有办法直接获取到 Consumer 是否 ack 了相应消息的通知了,只能通过组合使用 rabbitmq 的方案,实现类似的功能~
好的.明白了.谢谢楼主.
摩云飞
摩云飞

引用来自“ywjay”的评论

楼主.想问下rabbitmq能不能实现在消费者那里做处理完后进行通知生产者.使用ack的话好像不太好实现.因为如果没消费者的时候也会返回ack..我的是3.6.2版本的.好像已经没有immediate这个属性.
immediate 属性被去掉后,Producer 已经没有办法直接获取到 Consumer 是否 ack 了相应消息的通知了,只能通过组合使用 rabbitmq 的方案,实现类似的功能~
开源中国撞死人
开源中国撞死人
楼主.想问下rabbitmq能不能实现在消费者那里做处理完后进行通知生产者.使用ack的话好像不太好实现.因为如果没消费者的时候也会返回ack..我的是3.6.2版本的.好像已经没有immediate这个属性.
RabbitMQ 内存控制 硬盘控制

一、内存控制: vmmemoryhigh_watermark 该值为内存阈值,默认为0.4。意思为物理内存的40%。40%的内存并不是内存的最大的限制,它是一个发布的节制,当达到40%时Erlang会做GC。最坏的情况是使...

andrewniu ⋅ 05/10 ⋅ 0

openstack 最简单的 RabbitMQ 监控方法

先来看张图: 这是 Nova 的架构图,我们可以看到有两个组件处于架构的中心位置:数据库和Queue。数据库保存状态信息,而几乎所有的 nova-* 服务都直接依赖于 Queue 实现服务之间的通信和调用...

zhongbeida_xue ⋅ 05/09 ⋅ 0

RabbitMQ 3.7.5-beta.3 发布,带来多处 bug 修复

RabbitMQ 3.7.5-beta.3 发布,此版本是维护版本的预览版,主要带来了多处 bug 修复,涉及模块包括: Core Server CLI Tools Management Plugin LDAP Plugin Shovel Plugin Peer Discovery A...

雨田桑 ⋅ 04/30 ⋅ 0

RabbitMQ 3.7.5-rc.1 发布,bug 修复版本

RabbitMQ 3.7.5-rc.1 发布,此版本是维护版本的候选版本,主要是对一些 bug 进行了修复。 更新涉及模块包括: Core Server CLI Tools Management Plugin Federation Plugin LDAP Plugin Shov...

雨田桑 ⋅ 05/04 ⋅ 0

网易蜂巢微服务架构:用RabbitMQ实现轻量级通信

微服务架构与MQ RabbitMQ场景分析与优化 RabbitMQ在网易蜂巢中的应用和案例分享 1微服务架构与MQ 微服务架构是一种架构模式,它将单体应用划分成一组微小的服务,各服务之间使用轻量级的通信...

andrewniu ⋅ 05/10 ⋅ 0

Docker下RabbitMQ四部曲之四:高可用实战

本章是《Docker下RabbitMQ四部曲》系列的终篇,今天的我们一起来体验Rabbit’MQ集群的高可用能力,看看RabbitMQ集群中的部分节点宕机时,是否还能生产和消费消息; 原文地址:https://blog....

boling_cavalry ⋅ 05/19 ⋅ 0

Docker下RabbitMQ四部曲之二:细说RabbitMQ镜像制作

本章是《Docker下RabbitMQ四部曲》系列的第二篇,将详细简述Docker下制作RabbitMQ镜像的技术细节,包括以下内容: 1. 列举制作RabbitMQ镜像时用到的所有材料; 2. 编写Dockerfile; 3. 编写容...

boling_cavalry ⋅ 05/13 ⋅ 0

RabbitMQ 3.7.6-rc.2 发布,AMQP 消息服务器

RabbitMQ 3.7.6-rc.2 已发布,这是常规维护版本的候选版,主要包括错误修复。 本次更新包含如下内容: Core Server Bug Fixes Max priority cap for queues is now enforced and set to 255...

淡漠悠然 ⋅ 06/05 ⋅ 0

rabbitmq-server 安装

一,安装rabbitmq-server 1.安装erlang wget https://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm rpm -Uvh erlang-solutions-1.0-1.noarch.rpm rpm --import https:/......

丿小贰丶 ⋅ 05/08 ⋅ 0

Docker下RabbitMQ三部曲之一:极速体验(单机和集群)

从本章开始,我们一起在Docker环境实战RabbitMQ环境部署和对应的Java开发,当前是《Docker下RabbitMQ三部曲》系列的第一篇,整个三部曲由以下三篇文章组成: 1. 第一篇,即本章,我们用最快的...

boling_cavalry ⋅ 05/12 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

用SQL命令查看Mysql数据库大小

要想知道每个数据库的大小的话,步骤如下: 1、进入information_schema 数据库(存放了其他的数据库的信息) use information_schema; 2、查询所有数据的大小: select concat(round(sum(da...

源哥L ⋅ 27分钟前 ⋅ 0

两个小实验简单介绍@Scope("prototype")

实验一 首先有如下代码(其中@RestController的作用相当于@Controller+@Responsebody,可忽略) @RestController//@Scope("prototype")public class TestController { @RequestMap...

kalnkaya ⋅ 32分钟前 ⋅ 0

php-fpm的pool&php-fpm慢执行日志&open_basedir&php-fpm进程管理

12.21 php-fpm的pool pool是PHP-fpm的资源池,如果多个站点共用一个pool,则可能造成资源池中的资源耗尽,最终访问网站时出现502。 为了解决上述问题,我们可以配置多个pool,不同的站点使用...

影夜Linux ⋅ 41分钟前 ⋅ 0

微服务 WildFly Swarm 管理

Expose Application Metrics and Information 要公开关于我们的微服务的有用信息,我们需要做的就是将监视器模块添加到我们的pom.xml中: 这将使在管理和监视功能得到实现。从监控角度来看,...

woshixin ⋅ 41分钟前 ⋅ 0

java连接 mongo伪集群部署遇到的坑

部署mongo伪集群 #创建mongo数据存放文件地址mkdir -p /usr/local/config1/datamkdir -p /usr/local/config2/data mkdir -p /usr/local/config3/data mkdir -p /usr/local/config1/l......

努力爬坑人 ⋅ 42分钟前 ⋅ 0

React Native & Weex 区别

JS引擎 Weex使用V8, React native使用JSCore JS开发框架 ( Js Framework ) Weex基于vue.js(2W+ star)。小巧轻量的前端开发框架,组件化,数据绑定,2.0引入virtual dom。 ReactNative使用...

东东笔记 ⋅ 50分钟前 ⋅ 1

UIkit 分页组件动态加载简单实现

1. 问题描述 使用过UIkit分页组件的都清楚,UIkit的分页不能动态刷新数据,也就是不能在点击下一页的时候,动态从后台加载数据,并且刷新页数以及该页数上的数据,下面是一个简单实现,没有做...

影狼 ⋅ 51分钟前 ⋅ 0

Mobx入门之三:Provider && inject

上一节中<App/>组件传递状态temperatures给children -- <TemperatureInput />,如果组建是一个tree, 那么属性的传递则会非常繁琐。redux使用Provider给子组件提供store, connect将子组件和s...

pengqinmm ⋅ 53分钟前 ⋅ 0

魔兽世界 7.0版本 S23/S24/S25全职业普通+精锐套

  死亡骑士   (联盟)   (部落)   (精锐)   恶魔猎手   (联盟)   (部落)   (精锐)   德鲁伊   (联盟)   (部落)   (精锐)   猎人   (联盟) ...

wangchen1999 ⋅ 今天 ⋅ 0

maven顶级pom和子pom的版本号批量修改

当一个版本发布,新起一个版本时,我们只需要手动修改一下项目中pom.xml的版本号就可以了。但是如果这个maven项目有很多的子模块项目,那么一个个手动的去改就显得费时费力又繁琐了。还好,m...

ArlenXu ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部