Erlang tool -- lager overload protection
log 这个事, 说大不大说小又不小. 大点的, 可以用scribe flume 这样的系统去做, 小点的, 也就打印一个调试信息而已. 在Erlang 中, log 这事情确实比较伤, error_logger 是个单点, io:format 容易导致节点崩溃. 在开源社区, lager 算是使用比较广泛的一个, 然而, 同样不能完全避免单点的问题. 因为在lager 中, lager_event 作为 core, 是单个进程存在的.
dispatch_log(Severity, Metadata, Format, Args, Size) when is_atom(Severity)->
SeverityAsInt=lager_util:level_to_num(Severity),
case {whereis(lager_event), lager_config:get(loglevel, {?LOG_NONE, []})} of
{undefined, _} ->
{error, lager_not_running};
{Pid, {Level, Traces}} when (Level band SeverityAsInt) /= 0 orelse Traces /= [] ->
do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Pid);
_ ->
ok
end.
也就是在lager 中, 所有的log 都需要经过 lager_event 进程.
但是在lager 中, 有过载保护机制.
Prior to lager 2.0, the
gen_event
at the core of lager operated purely in synchronous mode. Asynchronous mode is faster, but has no protection against message queue overload. In lager 2.0, thegen_event
takes a hybrid approach. it polls its own mailbox size and toggles the messaging between synchronous and asynchronous depending on mailbox size.{async_threshold, 20},
{async_threshold_window, 5}This will use async messaging until the mailbox exceeds 20 messages, at which point synchronous messaging will be used, and switch back to asynchronous, when size reduces to
20 - 5 = 15
.If you wish to disable this behaviour, simply set it to
undefined
. It defaults to a low number to prevent the mailbox growing rapidly beyond the limit and causing problems. In general, lager should process messages as fast as they come in, so getting 20 behind should be relatively exceptional anyway.If you want to limit the number of messages per second allowed from
error_logger
, which is a good idea if you want to weather a flood of messages when lots of related processes crash, you can set a limit:{error_logger_hwm, 50}It is probably best to keep this number small.
简单点说就是, 相比较同步模式, 异步模式很快, 但是容易导致lager_event 单进程 message_queue 过大. 再且, 若设计使用不当, 导致lager_event 进程 large_heap 使整个VM崩溃也是分分钟的事.
现在, lager 采用的是hybrid 的方式, 也就是当lager_event 的message_queue_len 小于某个值时, 采用异步模式; 若message_queue_len 大于某个值, 就是用同步的方式. 很好理解, 继续瞧瞧内部的怎么实现的:
1, lager_backend_throttle 的启动
lager_backend_throttle 进程在lager_app 模块的start/2 启动, 也就是整个lager application 启动的时候.
{ok, Threshold} when is_integer(Threshold), Threshold >= 0 ->
DefWindow = erlang:trunc(Threshold * 0.2), % maybe 0?
ThresholdWindow =
case application:get_env(lager, async_threshold_window) of
undefined ->
DefWindow;
{ok, Window} when is_integer(Window), Window < Threshold, Window >= 0 ->
Window;
{ok, BadWindow} ->
error_logger:error_msg(
"Invalid value for 'async_threshold_window': ~p~n", [BadWindow]),
throw({error, bad_config})
end,
_ = supervisor:start_child(lager_handler_watcher_sup,
[lager_event, lager_backend_throttle, [Threshold, ThresholdWindow]]),
ok;
简单易懂的代码,就不需要过多的解释了.
2, lager_backend_throttle 的作用
lager_backend_throttle 是 lager_event event server 中的一个handler , 会处理 message-tag 为log 的所有event, 但仅仅是全量接受而已, 不做实际的log 处理.
handle_event({log, _Message},State) ->
{message_queue_len, Len} = erlang:process_info(self(), message_queue_len),
case {Len > State#state.hwm, Len < State#state.window_min, State#state.async} of
{true, _, true} ->
%% need to flip to sync mode
lager_config:set(async, false),
{ok, State#state{async=false}};
{_, true, false} ->
%% need to flip to async mode
lager_config:set(async, true),
{ok, State#state{async=true}};
_ ->
%% nothing needs to change
{ok, State}
end;
在接收到 message-tag 为 log 的 event 后, 通过process_info/2 函数获取当前的message_queue_len, 然后切换 async 模式.
3, async 模式的影响
async sync 模式会在message_queue_len 增大/降低过程中切换, 最终会对log 的notify 方式产生不同的影响.
do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, Pid) when is_atom(Severity) ->
Destinations = case TraceFilters of
[] ->
[];
_ ->
lager_util:check_traces(Metadata,SeverityAsInt,TraceFilters,[])
end,
case (LevelThreshold band SeverityAsInt) /= 0 orelse Destinations /= [] of
true ->
Msg = case Args of
A when is_list(A) ->
safe_format_chop(Format,Args,Size);
_ ->
Format
end,
LagerMsg = lager_msg:new(Msg,
Severity, Metadata, Destinations),
case lager_config:get(async, false) of
true ->
gen_event:notify(Pid, {log, LagerMsg});
false ->
gen_event:sync_notify(Pid, {log, LagerMsg})
end;
false ->
ok
end.
在async 为 true 的情况下, 使用 gen_event:notify(异步方式) 将log 交给lager_event 进程, async 为false 的情况下, 则使用gen_event:sync_notify(同步方式)将 log 交给 lager_event 进程.
notify/sync_notify
1, notify
notify 是用户进程将Event 组装成 message-tag 为 notify 的消息, 通过erlang:send/2 方式发给 event 进程(在lager 中也就是lager_event), 然后在event 进程中依次执行 handlers 中的handle_event callback 函数.
server_update(Handler1, Func, Event, SName) ->
Mod1 = Handler1#handler.module,
State = Handler1#handler.state,
case catch Mod1:Func(Event, State) of
{ok, State1} ->
{ok, Handler1#handler{state = State1}};
{ok, State1, hibernate} ->
{hibernate, Handler1#handler{state = State1}};
{swap_handler, Args1, State1, Handler2, Args2} ->
do_swap(Mod1, Handler1, Args1, State1, Handler2, Args2, SName);
remove_handler ->
do_terminate(Mod1, Handler1, remove_handler, State,
remove, SName, normal),
no;
Other ->
do_terminate(Mod1, Handler1, {error, Other}, State,
Event, SName, crash),
no
end.
2, sync_notify
sync_notify 是用户进程通过gen:call/4 函数调用event 进程, 消息的message-tag 为sync_notify. event 进程同样是依次执行handlers 中的handle_event callback 函数, 不同的是, 还会向用户进程reply 消息(ok).
highwatermark
在lager 中, error_logger_lager_h 作为 error_logger 的一个 handler, 主要用来限制每秒 from error_logger 的 message 数量.
同样是在lager application 启动的时候, 在start/2 中调用启动.
在每次接收到event 时, 就会执行check_hwm/1 函数:
check_hwm(State = #state{hwm = Hwm, lasttime = Last, dropped = Drop}) ->
%% are we still in the same second?
{M, S, _} = Now = os:timestamp(),
case Last of
{M, S, _} ->
%% still in same second, but have exceeded the high water mark
NewDrops = discard_messages(Now, 0),
{false, State#state{dropped=Drop+NewDrops}};
_ ->
%% different second, reset all counters and allow it
case Drop > 0 of
true ->
?LOGFMT(warning, self(), "lager_error_logger_h dropped ~p messages in the last second that exceeded the limit of ~p messages/sec",
[Drop, Hwm]);
false ->
ok
end,
{true, State#state{dropped = 0, mps=1, lasttime = Now}}
end.
总结:
1, sync 模式速度慢, async 速度快, 但是不利于稳定系统的负载;
2, log 这事说大不大, 说小真不小; 本地的log 也就是用来调试和debug 来用, 正经的还是应该使用外部的日志系统;
3, 认真了解application 的每一个参数, 对于参数的设定才更有把握.
Erlang tool -- lager overload protection的更多相关文章
- Erlang tool -- recon
遇见recon 以来, 每次定位系统瓶颈, 总是能让我眼前一亮. 比如说, 定位非尾递归导致的内存暴涨, 定位引发CPU满载的进程.得心应手,每每额手称庆. recon 是ferd 大神 释出的一个 ...
- Erlang库 -- 有意思的库汇总
抄自这里 首先,库存在的目的大致可分为:1.提供便利2.尽可能解决一些痛点 首先,我们先明确一下Erlang编程语言的一些痛点(伪痛点):1,单进程问题Erlang虚拟机属于抢占式调度,抢占式调度有很 ...
- Linux/centos下安装riak
必备的组件: gccgcc-c++glibc-develmakepam-devel 使用yum安装相关组件 sudo yum install gcc gcc-c++ glibc-devel make ...
- golang--gopher北京大会(2)(rework)
三.七牛老许 qlang: github qiniu/qlang microservice architecture: http://martinfowler.com/articles/microse ...
- {ICIP2014}{收录论文列表}
This article come from HEREARS-L1: Learning Tuesday 10:30–12:30; Oral Session; Room: Leonard de Vinc ...
- Using Sessions and Session Persistence---reference
Using Sessions and Session Persistence The following sections describe how to set up and use session ...
- 关于Weblogic Server(介绍)
Weblogic, 美国Oracle公司名下产品,是一个基于 J2EE 架构.可扩展的应用服务器. 本文档选取部分官方文档翻译 总览 支持多种类型的分布式应用 基于 SOA 应用的理想架构 完整实现 ...
- RHM-M60型挖掘机力矩限制器/载荷指示器
RHM-M60挖掘机力矩限制器RHM-M60 excavator crane moment limiter RHM-M60型挖掘机力矩限制器是臂架型起重机机械的安全保护装置,本产品采用32位高 ...
- [转]The Production Environment at Google (part 2)
How the production environment at Google fits together for networking, monitoring and finishing with ...
随机推荐
- 跨平台移动开发_PhoneGap API Camera 使用摄像头采集照片.
camera对象提供对设备默认摄像头应用程序的访问. 程序运行效果 相关代码 <!DOCTYPE html> <html> <head> <title> ...
- body-parser小解
body-parser用来解析http请求体,对不同的content-type有不同的处理方式, 首先介绍一下常见的四种Content-Type: 1.application/x-www-form-u ...
- mysql中的一些操作
查询mysql中事务提交的情况: show variables like '%commit%'; 可以查看当前autocommit值 在mysql数据库中它的默认值是"on"代表自 ...
- MongoDB快速入门(一)
MongoDB是一个跨平台,面向文档的数据库,提供高性能,高可用性和易于扩展.MongoDB是工作在集合和文档上一种概念. 数据数 数据库是一个集合的物理容器.每个数据库获取其自己设定在文件系统上的文 ...
- 深入剖析JDK动态代理源码实现
动态代理.静态代理优缺点优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性.这是代理的共有优点.动态代理只有在用到被代理对象的时候才会对被代理类进行类加载. 而静态代理在编译器就已经开始占内存了 ...
- sqlserver 实时同步(发布订阅)
配置发布订阅手册 不同版本须知:https://www.sqlmanager.net/en/articles/1548 向后兼容性:参考https://docs.microsoft.com/zh-cn ...
- Hadoop的Docker镜像构建
1.Dockerfile ###Dockerfile -- beagin FROM ubuntu:trusty #MAINTAINER The Hue Team "https://githu ...
- Thinkpad个性化设置:F1~F12恢复正常按键,Fn与Ctrl按键互换
一.F1~F12恢复正常按键 联想Thinkpad的F1~F12键功能与其他笔记本是相反的! 也就是说,如果不按着Fn,按那几个功能键,实现的是属性设置的功能,比如直接按下F1键是静音,F2键是音量降 ...
- sp_rename sqlserver 表 列 索引 类型重命名
--[语法] sp_rename [ @objname = ] 'object_name' , [ @newname = ] 'new_name' [ , [ @objtype = ] 'object ...
- WebServers 异步
在网上也找了一些,发现要么题目和内容不对应,明明是在Winform下异步调用,却写成在Asp.net异步调用WebService,有的调用方式在.NET3.5下不能通过,.NET3.5下取消了Begi ...