我正在寻找我的凤凰应用程序花费CPU周期的地方。人们建议我看看:observer工具,但是我无法解释它的输出。请参阅以下屏幕:
应用程序的简要描述:它接受使用JWT的REST调用,并根据JWT中的用户将调用转发给后端服务器。
我假设'Reds'列或多或少对应于cpu使用情况。所以:
作为参考,这里是类似testrun的etop输出,如何让它显示时间值而不是' - '?我想要那个吗?
Pid Name or Initial Func Time Reds Memory MsgQ Current Function
----------------------------------------------------------------------------------------
<0.247.0> 'Elixir.Phoenix.Code '-'24468015 2178040 13 gen:do_call/4
<0.46.0> file_server_2 '-'14085904 197128 0 gen_server:loop/7
<0.642.0> hackney_pool:init/1 '-' 795169 14368 0 gen_server:loop/7
<0.416.0> jose_server '-' 483301 18728 0 gen_server:loop/7
<0.323.0> 'Elixir.Logger' '-' 254592 30680 0 gen_event:fetch_msg/
<0.96.0> 'Elixir.Mix.ProjectS '-' 184593 689560 0 gen_server:loop/7
<0.374.0> hackney_manager '-' 155632 125504 0 gen_server:loop/7
<0.3121.0> cowboy_protocol:init '-' 132982 47368 0 gen:do_call/4
<0.3117.0> cowboy_protocol:init '-' 132979 47368 0 gen:do_call/4
<0.3119.0> cowboy_protocol:init '-' 132978 47368 0 gen:do_call/4
<0.3120.0> cowboy_protocol:init '-' 132978 47368 0 gen:do_call/4
答案 0 :(得分:-1)
总结我的发现:
原来我运行的是:dev模式,在开发模式下配置不同。例如,您可以编辑源代码,在下一个HTTP请求中,代码重新加载器将检测更改并快速重新编译代码。因此,当我使用请求对服务器进行攻击时,Code Reloader会不断检查我的所有源文件,这也解释了为什么file_server正好在它下面。我切换到生产模式(MIX_ENV = prod)并且性能增加了三倍。
关于Logger:我已将loglevel设置为:error并且未显示输出。但是,我喜欢做很多日志记录,而我的日志语句几乎描述了程序中做出的所有决策,因此对于每个HTTP请求,代码中都存在数十个日志语句。这往往会加起来,特别是如果你每秒发出数百个请求。
值得庆幸的是,您可以告诉Elixir编译器从生产中完全删除这些语句:在记录器配置中使用compile_time_purge_level标志(在config/prod.exs
中):
# Do not print debug messages in production
config :logger, level: :warn, compile_time_purge_level: :info
最后我使用fprof
分析器,它确定JWT处理是罪魁祸首,我决定停在那里。
以下是如何做到这一点:
iex(5)> :fprof.trace([:start, {:procs, :all}])
:ok
iex(6)> :fprof.trace(:stop)
:ok
iex(7)> :fprof.profile
...................................,
(and a whole lot more lines)
End of trace!
:ok
iex(8)> :fprof.analyse([callers: true, sort: :own, totals: true, details: true])
(followed by > 1000 lines of output)
Done!
:ok
请注意,fprof会生成大量输出文件(当前目录中的fprof.trace),即使只跟踪一分钟也是如此。
这是fprof.analyse()调用的第一部分:
iex(5)> :fprof.analyse([callers: true,sort: :own,totals: true,details: true])
Processing data...
Creating output...
%% Analysis results:
{ analysis_options,
[{callers, true},
{sort, own},
{totals, true},
{details, true}]}.
% CNT ACC OWN
[{ totals, 4774694,17162.356,14837.297}]. %%%
{[{{base64url,'-decode/1-lbc$^0/2-0-',2}, 206492, 0.000, 829.718},
{{base64url,decode,1}, 1083, 1191.294, 5.680}],
{ {base64url,'-decode/1-lbc$^0/2-0-',2}, 207575, 1191.294, 835.398}, % <----------
[{{base64url,'-decode/1-lbc$^0/2-0-',2}, 206492, 0.000, 829.718},
{{base64url,urldecode_digit,1}, 206492, 350.686, 348.324},
{garbage_collect, 620, 5.210, 5.210}]}.
{[{{hackney_bstr,'-to_lower/1-lbc$^0/2-0-',2}, 170031, 0.000, 685.779},
{{hackney_bstr,to_lower,1}, 14079, 1070.261, 62.526}],
{ {hackney_bstr,'-to_lower/1-lbc$^0/2-0-',2}, 184110, 1070.261, 748.305}, % <----------
[{{hackney_bstr,'-to_lower/1-lbc$^0/2-0-',2}, 170031, 0.000, 685.779},
{{hackney_bstr,char_to_lower,1}, 170031, 306.065, 305.583},
{garbage_collect, 970, 15.891, 15.891}]}.
ACC列表示累计时间,这是每个函数在此函数中花费的总时间,包括子函数。 OWN列是相同的,但没有子功能。通常,您希望按OWN排序以检查哪些功能占用了大部分CPU。
注意输出被分成块,并且在每个块的中间有一行以'%'符号结尾。这是每个块正在讨论的功能,其他行是父项,以及此功能的子项。从这里开始,你就是自己。
在我的情况下,fprof输出显示没有更多关于性能的异常值,所以这就是我停止的地方。