diff --git a/apps/emqx/src/emqx_cpu_sup_worker.erl b/apps/emqx/src/emqx_cpu_sup_worker.erl new file mode 100644 index 000000000..15b4ad758 --- /dev/null +++ b/apps/emqx/src/emqx_cpu_sup_worker.erl @@ -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}. diff --git a/apps/emqx/src/emqx_os_mon.erl b/apps/emqx/src/emqx_os_mon.erl index 1cc1e8469..6c5d9843b 100644 --- a/apps/emqx/src/emqx_os_mon.erl +++ b/apps/emqx/src/emqx_os_mon.erl @@ -18,6 +18,7 @@ -behaviour(gen_server). +-include("emqx.hrl"). -include("logger.hrl"). -export([start_link/0]). @@ -47,8 +48,6 @@ ]). -export([is_os_check_supported/0]). --include("emqx.hrl"). - -define(OS_MON, ?MODULE). start_link() -> @@ -92,6 +91,8 @@ handle_continue(setup, undefined) -> SysHW = init_os_monitor(), MemRef = start_mem_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}}. 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) -> CPUHighWatermark = emqx:get_config([sysmon, os, cpu_high_watermark]) * 100, CPULowWatermark = emqx:get_config([sysmon, os, cpu_low_watermark]) * 100, - CPUVal = emqx_vm:cpu_util(), + CPUVal = cpu_sup:util(), case CPUVal of %% 0 or 0.0 Busy when Busy == 0 -> diff --git a/apps/emqx/src/emqx_sys_sup.erl b/apps/emqx/src/emqx_sys_sup.erl index 8055d6d56..eb456be90 100644 --- a/apps/emqx/src/emqx_sys_sup.erl +++ b/apps/emqx/src/emqx_sys_sup.erl @@ -28,7 +28,7 @@ start_link() -> init([]) -> OsMon = 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 -> [] end, Children = diff --git a/apps/emqx/src/emqx_vm.erl b/apps/emqx/src/emqx_vm.erl index 2ca58b6d2..5877ab184 100644 --- a/apps/emqx/src/emqx_vm.erl +++ b/apps/emqx/src/emqx_vm.erl @@ -16,6 +16,8 @@ -module(emqx_vm). +-include("logger.hrl"). + -export([ schedulers/0, scheduler_usage/1, @@ -376,28 +378,29 @@ avg15() -> compat_windows(fun cpu_sup:avg15/0). cpu_util() -> - compat_windows(fun cpu_sup:util/0). + compat_windows(fun() -> emqx_cpu_sup_worker:cpu_util() end). cpu_util(Args) -> - compat_windows(fun cpu_sup:util/1, Args). + compat_windows(fun() -> emqx_cpu_sup_worker:cpu_util(Args) end). +-spec compat_windows(function()) -> any(). +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_number(Val) -> Val; + Val when is_tuple(Val) -> Val; + _ -> 0.0 + catch + _:_ -> 0.0 + end; + false -> + 0.0 + end; compat_windows(Fun) -> - case compat_windows(Fun, []) of - Val when is_float(Val) -> floor(Val * 100) / 100; - Val when is_number(Val) -> Val; - _ -> 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 - _:_ -> 0.0 - end. + ?SLOG(warning, "Invalid function: ~p", [Fun]), + error({badarg, Fun}). load(Avg) -> floor((Avg / 256) * 100) / 100. diff --git a/apps/emqx_management/src/emqx_mgmt.erl b/apps/emqx_management/src/emqx_mgmt.erl index e470805d8..992905e48 100644 --- a/apps/emqx_management/src/emqx_mgmt.erl +++ b/apps/emqx_management/src/emqx_mgmt.erl @@ -205,23 +205,17 @@ cpu_stats() -> false -> []; true -> - Idle = vm_stats('cpu.idle'), - [ - {cpu_idle, Idle}, - {cpu_use, 100 - Idle} - ] + vm_stats('cpu') end. -vm_stats('cpu.idle') -> - case emqx_vm:cpu_util([detailed]) of - {_Num, _Use, List, _} when is_list(List) -> proplists:get_value(idle, List, 0); - %% return {all, 0, 0, []} when cpu_sup is not started - _ -> 0 - end; -vm_stats('cpu.use') -> - case vm_stats('cpu.idle') of - 0 -> 0; - Idle -> 100 - Idle +vm_stats('cpu') -> + CpuUtilArg = [], + case emqx_vm:cpu_util([CpuUtilArg]) of + %% return 0.0 when `emqx_cpu_sup_worker` is not started + {all, Use, Idle, _} -> + [{cpu_use, Use}, {cpu_idle, Idle}]; + _ -> + [{cpu_use, 0}, {cpu_idle, 0}] end; vm_stats('total.memory') -> {_, MemTotal} = get_sys_memory(),