diff options
author | Marek Majkowski <majek@lshift.net> | 2009-09-25 17:31:13 +0100 |
---|---|---|
committer | Marek Majkowski <majek@lshift.net> | 2009-09-25 17:31:13 +0100 |
commit | a7a34f7bc76bb4b4cb1e37adfeb4a41e31381294 (patch) | |
tree | cd2dde5adc439d93da592884fee7db8790c45be9 | |
parent | 08c140a80ca86338fbfcdc88219a1dd1507070a5 (diff) | |
download | rabbitmq-server-a7a34f7bc76bb4b4cb1e37adfeb4a41e31381294.tar.gz |
qa fixes, some refactoring
-rw-r--r-- | ebin/rabbit_app.in | 3 | ||||
-rwxr-xr-x | scripts/rabbitmq-server | 8 | ||||
-rwxr-xr-x | scripts/rabbitmq-server.bat | 8 | ||||
-rwxr-xr-x | scripts/rabbitmq-service.bat | 8 | ||||
-rw-r--r-- | src/rabbit.erl | 10 | ||||
-rw-r--r-- | src/rabbit_alarm.erl | 12 | ||||
-rw-r--r-- | src/rabbit_memguard.erl | 187 |
7 files changed, 156 insertions, 80 deletions
diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 8796b425..9dea663a 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -21,5 +21,4 @@ {default_user, <<"guest">>}, {default_pass, <<"guest">>}, {default_vhost, <<"/">>}, - {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}, - {memory_high_watermark, 0.85}]}]}. + {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}]}]}. diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index ffd97b35..dcc23f79 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -100,12 +100,8 @@ exec erl \ -sasl errlog_type error \ -kernel error_logger '{file,"'${RABBITMQ_LOGS}'"}' \ -sasl sasl_error_logger '{file,"'${RABBITMQ_SASL_LOGS}'"}' \ - -os_mon start_cpu_sup true \ - -os_mon start_disksup false \ - -os_mon start_memsup true \ - -os_mon start_os_sup false \ - -os_mon memsup_system_only false \ - -os_mon system_memory_high_watermark 0.95 \ + -os_mon start_memsup false \ + -os_mon system_memory_high_watermark 0.70 \ -mnesia dir "\"${RABBITMQ_MNESIA_DIR}\"" \ ${RABBITMQ_CLUSTER_CONFIG_OPTION} \ ${RABBITMQ_SERVER_START_ARGS} \ diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index b83d44ca..3f5ac3e7 100755 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -125,12 +125,8 @@ if exist "%RABBITMQ_EBIN_ROOT%\rabbit.boot" ( %RABBITMQ_SERVER_ERL_ARGS% ^
-sasl errlog_type error ^
-sasl sasl_error_logger {file,\""%RABBITMQ_LOG_BASE%/%RABBITMQ_NODENAME%-sasl.log"\"} ^
--os_mon start_cpu_sup true ^
--os_mon start_disksup false ^
--os_mon start_memsup true ^
--os_mon start_os_sup false ^
--os_mon memsup_system_only false ^
--os_mon system_memory_high_watermark 1.0 ^
+-os_mon start_memsup false ^
+-os_mon system_memory_high_watermark 0.7 ^
-mnesia dir \""%RABBITMQ_MNESIA_DIR%"\" ^
%CLUSTER_CONFIG% ^
%RABBITMQ_SERVER_START_ARGS% ^
diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index dcb929b1..480acdda 100755 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -170,12 +170,8 @@ set ERLANG_SERVICE_ARGUMENTS= ^ -kernel error_logger {file,\""%RABBITMQ_LOG_BASE%/%RABBITMQ_NODENAME%.log"\"} ^
-sasl errlog_type error ^
-sasl sasl_error_logger {file,\""%RABBITMQ_LOG_BASE%/%RABBITMQ_NODENAME%-sasl.log"\"} ^
--os_mon start_cpu_sup true ^
--os_mon start_disksup false ^
--os_mon start_memsup true ^
--os_mon start_os_sup false ^
--os_mon memsup_system_only false ^
--os_mon system_memory_high_watermark 1.0 ^
+-os_mon start_memsup false ^
+-os_mon system_memory_high_watermark 0.7 ^
-mnesia dir \""%RABBITMQ_MNESIA_DIR%"\" ^
%CLUSTER_CONFIG% ^
%RABBITMQ_SERVER_START_ARGS% ^
diff --git a/src/rabbit.erl b/src/rabbit.erl index 875021aa..7df187cf 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -137,7 +137,9 @@ start(normal, []) -> check_empty_content_body_frame_size(), ok = rabbit_alarm:start(), - case application:get_env(memory_high_watermark) of + MemoryWatermark = + application:get_env(os_mon, system_memory_high_watermark), + case MemoryWatermark of {ok, false} -> ok; {ok, off} -> ok; {ok, Float} -> start_child(rabbit_memguard, [Float]) @@ -251,10 +253,7 @@ print_banner() -> io:nl(). start_child(Mod) -> - {ok,_} = supervisor:start_child(rabbit_sup, - {Mod, {Mod, start_link, []}, - transient, 100, worker, [Mod]}), - ok. + start_child(Mod, []). start_child(Mod, Args) -> {ok,_} = supervisor:start_child(rabbit_sup, @@ -262,7 +261,6 @@ start_child(Mod, Args) -> transient, 100, worker, [Mod]}), ok. - ensure_working_log_handlers() -> Handlers = gen_event:which_handlers(error_logger), ok = ensure_working_log_handler(error_logger_file_h, diff --git a/src/rabbit_alarm.erl b/src/rabbit_alarm.erl index c1997554..5abd10bd 100644 --- a/src/rabbit_alarm.erl +++ b/src/rabbit_alarm.erl @@ -38,7 +38,7 @@ -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). --record(alarms, {alertees, memory_high_watermark = false}). +-record(alarms, {alertees, vm_memory_high_watermark = false}). %%---------------------------------------------------------------------------- @@ -72,7 +72,7 @@ init([]) -> handle_call({register, Pid, HighMemMFA}, State = #alarms{alertees = Alertess}) -> _MRef = erlang:monitor(process, Pid), - case State#alarms.memory_high_watermark of + case State#alarms.vm_memory_high_watermark of true -> {M, F, A} = HighMemMFA, ok = erlang:apply(M, F, A ++ [Pid, true]); false -> ok @@ -83,13 +83,13 @@ handle_call({register, Pid, HighMemMFA}, handle_call(_Request, State) -> {ok, not_understood, State}. -handle_event({set_alarm, {memory_high_watermark, []}}, State) -> +handle_event({set_alarm, {vm_memory_high_watermark, []}}, State) -> ok = alert(true, State#alarms.alertees), - {ok, State#alarms{memory_high_watermark = true}}; + {ok, State#alarms{vm_memory_high_watermark = true}}; -handle_event({clear_alarm, memory_high_watermark}, State) -> +handle_event({clear_alarm, vm_memory_high_watermark}, State) -> ok = alert(false, State#alarms.alertees), - {ok, State#alarms{memory_high_watermark = false}}; + {ok, State#alarms{vm_memory_high_watermark = false}}; handle_event(_Event, State) -> {ok, State}. diff --git a/src/rabbit_memguard.erl b/src/rabbit_memguard.erl index c543876d..29e21bdc 100644 --- a/src/rabbit_memguard.erl +++ b/src/rabbit_memguard.erl @@ -52,13 +52,20 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([update/0]). +-export([update/0, get_total_memory/1, + get_check_interval/0, set_check_interval/1, + get_vm_memory_high_watermark/0, set_vm_memory_high_watermark/1]). -define(SERVER, rabbit_memguard). -define(DEFAULT_MEMORY_CHECK_INTERVAL, 1000). --record(state, {memory_limit, +%% For unknown OS, we assume that we have 512 MB of memory, which is pretty +%% safe value, even for 32 bit systems. It's better to be slow than to crash. +-define(MEMORY_SIZE_FOR_UNKNOWN_OS, 512*1024*1024). + +-record(state, {total_memory, + memory_limit, timeout, timer, alarmed @@ -73,9 +80,97 @@ update() -> gen_server2:cast(?SERVER, update). %%---------------------------------------------------------------------------- -get_system_memory_data(Key) -> - dict:fetch(Key, - dict:from_list( memsup:get_system_memory_data() )). +%% get_total_memory(OS) -> Total +%% Windows and Freebsd code based on: memsup:get_memory_usage/1 +%% Original code was part of OTP and released under "Erlang Public License". + +%% Darwin: Uses vm_stat command. +get_total_memory({unix,darwin}) -> + File = os:cmd("/usr/bin/vm_stat"), + Lines = string:tokens(File, "\n"), + Dict = dict:from_list(lists:map(fun parse_line_mach/1, Lines)), + [PageSize, Inactive, Active, Free, Wired] = + [dict:fetch(Key, Dict) || + Key <- [page_size, 'Pages inactive', 'Pages active', 'Pages free', + 'Pages wired down']], + MemTotal = PageSize * (Inactive + Active + Free + Wired), + MemTotal; + +%% FreeBSD: Look in /usr/include/sys/vmmeter.h for the format of struct +%% vmmeter +get_total_memory({unix,freebsd}) -> + PageSize = freebsd_sysctl("vm.stats.vm.v_page_size"), + PageCount = freebsd_sysctl("vm.stats.vm.v_page_count"), + NMemTotal = PageCount * PageSize, + NMemTotal; + +%% Win32: Find out how much memory is in use by asking +%% the os_mon_sysinfo process. +get_total_memory({win32,_OSname}) -> + [Result|_] = os_mon_sysinfo:get_mem_info(), + {ok, [_MemLoad, TotPhys, _AvailPhys, + _TotPage, _AvailPage, _TotV, _AvailV], _RestStr} = + io_lib:fread("~d~d~d~d~d~d~d", Result), + TotPhys; + +%% Linux: Look in /proc/meminfo +get_total_memory({unix, linux}) -> + File = read_proc_file("/proc/meminfo"), + Lines = string:tokens(File, "\n"), + Dict = dict:from_list(lists:map(fun parse_line_linux/1, Lines)), + MemTotal = dict:fetch('MemTotal', Dict), + MemTotal; + +get_total_memory(_OsType) -> + unknown. + +-define(BUFFER_SIZE, 1024). + +%% A line looks like "Foo bar: 123456." +parse_line_mach(Line) -> + [Name, RHS | _Rest] = string:tokens(Line, ":"), + case Name of + "Mach Virtual Memory Statistics" -> + ["(page", "size", "of", PageSize, "bytes)"] = + string:tokens(RHS, " "), + {page_size, list_to_integer(PageSize)}; + _ -> + [Value | _Rest1] = string:tokens(RHS, " ."), + {list_to_atom(Name), list_to_integer(Value)} + end. + +freebsd_sysctl(Def) -> + list_to_integer(os:cmd("/sbin/sysctl -n " ++ Def) -- "\n"). + +%% file:read_file does not work on files in /proc as it seems to get +%% the size of the file first and then read that many bytes. But files +%% in /proc always have length 0, we just have to read until we get +%% eof. +read_proc_file(File) -> + {ok, IoDevice} = file:open(File, [read, raw]), + Res = read_proc_file(IoDevice, []), + file:close(IoDevice), + lists:flatten(lists:reverse(Res)). + +read_proc_file(IoDevice, Acc) -> + case file:read(IoDevice, ?BUFFER_SIZE) of + {ok, Res} -> read_proc_file(IoDevice, [Res | Acc]); + eof -> Acc + end. + +%% A line looks like "FooBar: 123456 kB" +parse_line_linux(Line) -> + [Name, RHS | _Rest] = string:tokens(Line, ":"), + [Value | UnitsRest] = string:tokens(RHS, " "), + Value1 = case UnitsRest of + [] -> list_to_integer(Value); %% no units + ["kB"] -> list_to_integer(Value) * 1024 + end, + {list_to_atom(Name), Value1}. + + + +%%---------------------------------------------------------------------------- %% On a 32-bit machine, if you're using more than 2 gigs of RAM %% you're in big trouble anyway. @@ -85,29 +180,32 @@ get_vm_limit() -> 8 -> infinity end. - min(A,B) -> case A<B of true -> A; false -> B end. -mem_fraction_to_limit(MemFraction) -> - get_system_memory_data(system_total_memory) * MemFraction. - -mem_limit_to_fraction(MemLimit) -> - MemLimit / get_system_memory_data(system_total_memory). -get_mem_limit(MemFraction) -> - min(mem_fraction_to_limit(MemFraction), get_vm_limit()). +get_mem_limit(MemFraction, TotalMemory) -> + min(TotalMemory * MemFraction, get_vm_limit()). init([MemFraction]) -> - MemLimit = get_mem_limit(MemFraction), - rabbit_log:info("Memory alarm set to ~p.~n", [MemLimit]), - adjust_memsup_interval(?DEFAULT_MEMORY_CHECK_INTERVAL), + TotalMemory = case get_total_memory(os:type()) of + unknown -> + rabbit_log:info("Unknown total memory size for your OS ~p. " + "Assuming memory size is ~p bytes.~n", + [os:type(), ?MEMORY_SIZE_FOR_UNKNOWN_OS]), + ?MEMORY_SIZE_FOR_UNKNOWN_OS; + M -> M + end, + MemLimit = get_mem_limit(MemFraction, TotalMemory), + rabbit_log:info("Memory alarm set to ~p, ~p bytes.~n", + [MemFraction, MemLimit]), TRef = start_timer(?DEFAULT_MEMORY_CHECK_INTERVAL), - State = #state { memory_limit = MemLimit, + State = #state { total_memory = TotalMemory, + memory_limit = MemLimit, timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL, timer = TRef, alarmed = false}, @@ -117,12 +215,27 @@ start_timer(Timeout) -> {ok, TRef} = timer:apply_interval(Timeout, ?MODULE, update, []), TRef. -handle_call(get_memory_high_watermark, _From, State) -> - {reply, mem_limit_to_fraction(State#state.memory_limit), State}; -handle_call({set_memory_high_watermark, MemFraction}, _From, State) -> - MemLimit = get_mem_limit(MemFraction), - rabbit_log:info("Memory alarm set to ~p.~n", [MemLimit]), +get_check_interval() -> + gen_server2:call(?MODULE, get_check_interval). + +set_check_interval(Fraction) -> + gen_server2:call(?MODULE, {set_check_interval, Fraction}). + +get_vm_memory_high_watermark() -> + gen_server2:call(?MODULE, get_vm_memory_high_watermark). + +set_vm_memory_high_watermark(Fraction) -> + gen_server2:call(?MODULE, {set_vm_memory_high_watermark, Fraction}). + + +handle_call(get_vm_memory_high_watermark, _From, State) -> + {reply, State#state.memory_limit / State#state.total_memory, State}; + +handle_call({set_vm_memory_high_watermark, MemFraction}, _From, State) -> + MemLimit = get_mem_limit(MemFraction, State#state.total_memory), + rabbit_log:info("Memory alarm changed to ~p, ~p bytes.~n", + [MemFraction, MemLimit]), {reply, ok, State#state{memory_limit = MemLimit}}; handle_call(get_check_interval, _From, State) -> @@ -130,7 +243,6 @@ handle_call(get_check_interval, _From, State) -> handle_call({set_check_interval, Timeout}, _From, State) -> {ok, cancel} = timer:cancel(State#state.timer), - adjust_memsup_interval(Timeout), {reply, ok, State#state{timeout = Timeout, timer = start_timer(Timeout)}}; handle_call(_Request, _From, State) -> @@ -152,12 +264,8 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. emit_update_info(State, MemUsed, MemLimit) -> - {_Total, _Allocated, {_WorstPid, WorstAllocated}} - = memsup:get_memory_data(), - FreeMemory = get_system_memory_data(free_memory), - rabbit_log:info("memory_high_watermark ~p. Memory used:~p allowed:~p " - "heaviest_process:~p free:~p.~n", - [State, MemUsed, MemLimit, WorstAllocated, FreeMemory]). + rabbit_log:info("vm_memory_high_watermark ~p. Memory used:~p allowed:~p~n", + [State, MemUsed, MemLimit]). internal_update(State = #state { memory_limit = MemLimit, alarmed = Alarmed}) -> @@ -166,29 +274,12 @@ internal_update(State = #state { memory_limit = MemLimit, case {Alarmed, NewAlarmed} of {false, true} -> emit_update_info(set, MemUsed, MemLimit), - alarm_handler:set_alarm({memory_high_watermark, []}); + alarm_handler:set_alarm({vm_memory_high_watermark, []}); {true, false} -> emit_update_info(clear, MemUsed, MemLimit), - alarm_handler:clear_alarm(memory_high_watermark); + alarm_handler:clear_alarm(vm_memory_high_watermark); _ -> ok end, State #state {alarmed = NewAlarmed}. -adjust_memsup_interval(IntervalMs) -> - %% The default memsup check interval is 1 minute, which is way too - %% long - rabbit can gobble up all memory in a matter of seconds. - %% Unfortunately the memory_check_interval configuration parameter - %% and memsup:set_check_interval/1 function only provide a - %% granularity of minutes. So we have to peel off one layer of the - %% API to get to the underlying layer which operates at the - %% granularity of milliseconds. - %% - %% Note that the new setting will only take effect after the first - %% check has completed, i.e. after one minute. So if rabbit eats - %% all the memory within the first minute after startup then we - %% are out of luck. - ok = os_mon:call(memsup, - {set_check_interval, IntervalMs}, - infinity). - |