erlang观察者模型gen_event

erlang观察者模型gen_event

gen_event 和面向对象当中的 观察者有异曲同工之妙,本质上是将一个事件广播给N个观察者

1. 工作原理

step1.使用 add_handler或者 add_sup_handler 向事件源注册一个观察者( Observer)
step2.使用 notify 或者 sync_notify 向事件源发送一个事件(Event)
step3.事件源将Event广播给观察者列表Mod:handle_event

2. add_handler VS add_sup_handler

相同点:两者都能注册观察者
不同点:后者与调用proc相link,调用者proc挂掉之后,对应的观察者也丢失

3.notify VS sync_notify

两者的区别是cast与call的区别
前者:只是向事件源发送一条消息,不需要等返回值
后者:需要等返回值

用途:lager使用这个来限流

# lager_backend_throttle.erl
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
            ?TOGGLE_SYNC(),
            lager_config:set({State#state.sink, async}, false),
            {ok, State#state{async=false}};
        {_, true, false} ->
            %% need to flip to async mode
            ?TOGGLE_ASYNC(),
            lager_config:set({State#state.sink, async}, true),
            {ok, State#state{async=true}};
        _ ->
            %% nothing needs to change
            {ok, State}
    end;
    
# lager.erl 
case lager_config:get({Sink, async}, false) of
                true ->
                    gen_event:notify(SinkPid, {log, LagerMsg});
                false ->
                    gen_event:sync_notify(SinkPid, {log, LagerMsg})
            end,

使用当前的邮箱中的消息数量作为判断标准,一旦超过阀值,就将异步操作关掉,这样子在调用 lager:log(...)的时候就会阻塞住,从而达到限流的目的

但是换句话说,这种限流看上去没有什么意义,
只能对当前写日志的process限流
可以spawn别的proc持续的给目标proc的信箱塞消息

4.常见用途

用来作事件的广播,非常适合日志组件造轮子,比如lager,log4erl

5. 注意事项

由于事件广播的处理是串行的,所以尽量控制同一个事件源上面的观察者数量,由于在调度的时候对每个proc的时间片相对公平,所以观察者数量多了只有,处理变慢,消息堆积。

观察者最好不要进行耗时的处理,道理同上,如果有耗时的逻辑,请spawn另外的proc处理

如果有必要,请多开几个事件源.

总结

合理使用gen_event可以深入理解观察者模型,对事件源广播的处理变得简单。

你可能感兴趣的:(erlang观察者模型gen_event)