fix(vm): cpu usage/idle handled by single worker
This commit is contained in:
parent
f24a76e770
commit
207f38c42a
|
@ -0,0 +1,91 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_cpu_sup_worker).
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%% gen_server APIs
|
||||||
|
-export([start_link/0]).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
cpu_util/0,
|
||||||
|
cpu_util/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
%% gen_server callbacks
|
||||||
|
-export([
|
||||||
|
init/1,
|
||||||
|
handle_continue/2,
|
||||||
|
handle_call/3,
|
||||||
|
handle_cast/2,
|
||||||
|
terminate/2,
|
||||||
|
code_change/3
|
||||||
|
]).
|
||||||
|
|
||||||
|
-define(CPU_USAGE_WORKER, ?MODULE).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% API
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
cpu_util() ->
|
||||||
|
gen_server:call(?CPU_USAGE_WORKER, ?FUNCTION_NAME, infinity).
|
||||||
|
|
||||||
|
cpu_util(Args) ->
|
||||||
|
gen_server:call(?CPU_USAGE_WORKER, {?FUNCTION_NAME, Args}, infinity).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% gen_server callbacks
|
||||||
|
%% simply handle cpu_sup:util/0,1 called in one process
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
gen_server:start_link({local, ?CPU_USAGE_WORKER}, ?MODULE, [], []).
|
||||||
|
|
||||||
|
init([]) ->
|
||||||
|
{ok, undefined, {continue, setup}}.
|
||||||
|
|
||||||
|
handle_continue(setup, undefined) ->
|
||||||
|
%% start os_mon temporarily
|
||||||
|
{ok, _} = application:ensure_all_started(os_mon),
|
||||||
|
%% The returned value of the first call to cpu_sup:util/0 or cpu_sup:util/1 by a
|
||||||
|
%% process will on most systems be the CPU utilization since system boot,
|
||||||
|
%% but this is not guaranteed and the value should therefore be regarded as garbage.
|
||||||
|
%% This also applies to the first call after a restart of cpu_sup.
|
||||||
|
_Val = cpu_sup:util(),
|
||||||
|
{noreply, #{}}.
|
||||||
|
|
||||||
|
handle_call(cpu_util, _From, State) ->
|
||||||
|
Val = cpu_sup:util(),
|
||||||
|
{reply, Val, State};
|
||||||
|
handle_call({cpu_util, Args}, _From, State) ->
|
||||||
|
Val = erlang:apply(cpu_sup, util, Args),
|
||||||
|
{reply, Val, State};
|
||||||
|
handle_call(Req, _From, State) ->
|
||||||
|
{reply, {error, {unexpected_call, Req}}, State}.
|
||||||
|
|
||||||
|
handle_cast(Msg, State) ->
|
||||||
|
?SLOG(error, #{msg => "unexpected_cast", cast => Msg}),
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-include("emqx.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-export([start_link/0]).
|
-export([start_link/0]).
|
||||||
|
@ -47,8 +48,6 @@
|
||||||
]).
|
]).
|
||||||
-export([is_os_check_supported/0]).
|
-export([is_os_check_supported/0]).
|
||||||
|
|
||||||
-include("emqx.hrl").
|
|
||||||
|
|
||||||
-define(OS_MON, ?MODULE).
|
-define(OS_MON, ?MODULE).
|
||||||
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
|
@ -92,6 +91,8 @@ handle_continue(setup, undefined) ->
|
||||||
SysHW = init_os_monitor(),
|
SysHW = init_os_monitor(),
|
||||||
MemRef = start_mem_check_timer(),
|
MemRef = start_mem_check_timer(),
|
||||||
CpuRef = start_cpu_check_timer(),
|
CpuRef = start_cpu_check_timer(),
|
||||||
|
%% the value of the first call should be regarded as garbage.
|
||||||
|
_Val = cpu_sup:util(),
|
||||||
{noreply, #{sysmem_high_watermark => SysHW, mem_time_ref => MemRef, cpu_time_ref => CpuRef}}.
|
{noreply, #{sysmem_high_watermark => SysHW, mem_time_ref => MemRef, cpu_time_ref => CpuRef}}.
|
||||||
|
|
||||||
init_os_monitor() ->
|
init_os_monitor() ->
|
||||||
|
@ -131,7 +132,7 @@ handle_info({timeout, _Timer, mem_check}, #{sysmem_high_watermark := HWM} = Stat
|
||||||
handle_info({timeout, _Timer, cpu_check}, State) ->
|
handle_info({timeout, _Timer, cpu_check}, State) ->
|
||||||
CPUHighWatermark = emqx:get_config([sysmon, os, cpu_high_watermark]) * 100,
|
CPUHighWatermark = emqx:get_config([sysmon, os, cpu_high_watermark]) * 100,
|
||||||
CPULowWatermark = emqx:get_config([sysmon, os, cpu_low_watermark]) * 100,
|
CPULowWatermark = emqx:get_config([sysmon, os, cpu_low_watermark]) * 100,
|
||||||
CPUVal = emqx_vm:cpu_util(),
|
CPUVal = cpu_sup:util(),
|
||||||
case CPUVal of
|
case CPUVal of
|
||||||
%% 0 or 0.0
|
%% 0 or 0.0
|
||||||
Busy when Busy == 0 ->
|
Busy when Busy == 0 ->
|
||||||
|
|
|
@ -28,7 +28,7 @@ start_link() ->
|
||||||
init([]) ->
|
init([]) ->
|
||||||
OsMon =
|
OsMon =
|
||||||
case emqx_os_mon:is_os_check_supported() of
|
case emqx_os_mon:is_os_check_supported() of
|
||||||
true -> [child_spec(emqx_os_mon)];
|
true -> [child_spec(emqx_cpu_sup_worker), child_spec(emqx_os_mon)];
|
||||||
false -> []
|
false -> []
|
||||||
end,
|
end,
|
||||||
Children =
|
Children =
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
-module(emqx_vm).
|
-module(emqx_vm).
|
||||||
|
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
schedulers/0,
|
schedulers/0,
|
||||||
scheduler_usage/1,
|
scheduler_usage/1,
|
||||||
|
@ -376,28 +378,29 @@ avg15() ->
|
||||||
compat_windows(fun cpu_sup:avg15/0).
|
compat_windows(fun cpu_sup:avg15/0).
|
||||||
|
|
||||||
cpu_util() ->
|
cpu_util() ->
|
||||||
compat_windows(fun cpu_sup:util/0).
|
compat_windows(fun() -> emqx_cpu_sup_worker:cpu_util() end).
|
||||||
|
|
||||||
cpu_util(Args) ->
|
cpu_util(Args) ->
|
||||||
compat_windows(fun cpu_sup:util/1, Args).
|
compat_windows(fun() -> emqx_cpu_sup_worker:cpu_util(Args) end).
|
||||||
|
|
||||||
compat_windows(Fun) ->
|
-spec compat_windows(function()) -> any().
|
||||||
case compat_windows(Fun, []) of
|
compat_windows(Fun) when is_function(Fun, 0) ->
|
||||||
|
case emqx_os_mon:is_os_check_supported() of
|
||||||
|
true ->
|
||||||
|
try Fun() of
|
||||||
Val when is_float(Val) -> floor(Val * 100) / 100;
|
Val when is_float(Val) -> floor(Val * 100) / 100;
|
||||||
Val when is_number(Val) -> Val;
|
Val when is_number(Val) -> Val;
|
||||||
|
Val when is_tuple(Val) -> Val;
|
||||||
_ -> 0.0
|
_ -> 0.0
|
||||||
end.
|
|
||||||
|
|
||||||
compat_windows(Fun, Args) ->
|
|
||||||
try
|
|
||||||
case emqx_os_mon:is_os_check_supported() of
|
|
||||||
false -> 0.0;
|
|
||||||
true when Args =:= [] -> Fun();
|
|
||||||
true -> Fun(Args)
|
|
||||||
end
|
|
||||||
catch
|
catch
|
||||||
_:_ -> 0.0
|
_:_ -> 0.0
|
||||||
end.
|
end;
|
||||||
|
false ->
|
||||||
|
0.0
|
||||||
|
end;
|
||||||
|
compat_windows(Fun) ->
|
||||||
|
?SLOG(warning, "Invalid function: ~p", [Fun]),
|
||||||
|
error({badarg, Fun}).
|
||||||
|
|
||||||
load(Avg) ->
|
load(Avg) ->
|
||||||
floor((Avg / 256) * 100) / 100.
|
floor((Avg / 256) * 100) / 100.
|
||||||
|
|
|
@ -205,23 +205,17 @@ cpu_stats() ->
|
||||||
false ->
|
false ->
|
||||||
[];
|
[];
|
||||||
true ->
|
true ->
|
||||||
Idle = vm_stats('cpu.idle'),
|
vm_stats('cpu')
|
||||||
[
|
|
||||||
{cpu_idle, Idle},
|
|
||||||
{cpu_use, 100 - Idle}
|
|
||||||
]
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
vm_stats('cpu.idle') ->
|
vm_stats('cpu') ->
|
||||||
case emqx_vm:cpu_util([detailed]) of
|
CpuUtilArg = [],
|
||||||
{_Num, _Use, List, _} when is_list(List) -> proplists:get_value(idle, List, 0);
|
case emqx_vm:cpu_util([CpuUtilArg]) of
|
||||||
%% return {all, 0, 0, []} when cpu_sup is not started
|
%% return 0.0 when `emqx_cpu_sup_worker` is not started
|
||||||
_ -> 0
|
{all, Use, Idle, _} ->
|
||||||
end;
|
[{cpu_use, Use}, {cpu_idle, Idle}];
|
||||||
vm_stats('cpu.use') ->
|
_ ->
|
||||||
case vm_stats('cpu.idle') of
|
[{cpu_use, 0}, {cpu_idle, 0}]
|
||||||
0 -> 0;
|
|
||||||
Idle -> 100 - Idle
|
|
||||||
end;
|
end;
|
||||||
vm_stats('total.memory') ->
|
vm_stats('total.memory') ->
|
||||||
{_, MemTotal} = get_sys_memory(),
|
{_, MemTotal} = get_sys_memory(),
|
||||||
|
|
Loading…
Reference in New Issue