Erlang generic standard behaviours -- gen_server hibernate

时间:2022-06-01 16:42:04

hibernate 主要用于在内存空闲时,通过整理进程的stack,回收进程的heap 来达到回收内存节省资源的效果.

hibernate 可用于OTP 进程以及普通进程, hibernate 的官方文档 erlang:hibernate/3

Puts the calling process into a wait state where its memory allocation has been reduced as much as possible, which is useful if the process does not expect to receive any messages in the near future.

The process will be awaken when a message is sent to it, and control will resume in Module:Function with the arguments given by Argswith the call stack emptied, meaning that the process will terminate when that function returns. Thus erlang:hibernate/3 will never return to its caller.

If the process has any message in its message queue, the process will be awaken immediately in the same way as described above.

In more technical terms, what erlang:hibernate/3 does is the following. It discards the call stack for the process. Then it garbage collects the process. After the garbage collection, all live data is in one continuous heap. The heap is then shrunken to the exact same size as the live data which it holds (even if that size is less than the minimum heap size for the process).

If the size of the live data in the process is less than the minimum heap size, the first garbage collection occurring after the process has been awaken will ensure that the heap size is changed to a size not smaller than the minimum heap size.

Note that emptying the call stack means that any surrounding catch is removed and has to be re-inserted after hibernation. One effect of this is that processes started using proc_lib (also indirectly, such as gen_server processes), should use proc_lib:hibernate/3 instead to ensure that the exception handler continues to work when the process wakes up.

1, 主要用于某进程可能会在未来短期内空闲时

2, hibernate 会清空进程的stack 然后进程GC

从Erlang 进程PCB(process control block) 的角度来看, heap 主要用来存储复杂数据结构(如:tuples, lists, big integers), 而stack 主要存储简单的数据结构和指向在heap 中的复杂数据结构的引用. hibernate 清空进程stack 之后再进行进程GC ,会尽可能的保证heap 中的资源回收. 相比普通的GC 更彻底.

gen_server hibernate

gen_server module 对hibernate 调用的支持主要体现在 callback 返回参数(如:{reply, R, S, hibernate}) 以及enter_loop 函数.

 loop(Parent, Name, State, Mod, hibernate, Debug) ->
proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug]); wake_hib(Parent, Name, State, Mod, Debug) ->
Msg = receive
Input ->
Input
end,
decode_msg(Msg, Parent, Name, State, Mod, hibernate, Debug, true).

如果loop/6 函数的Timeout 参数值为 'hibernate', gen_server process 就会调用proc_lib:hibernate/3 函数使进程进入hibernate 状态. 而当有消息被发送至本进程时, 进程就会被resume并调用wake_hib/5 回到正常的decode_msg 流程.

而enter_loop 函数的作用是 make 一个已经存在的process 变成gen_server 进程:

The calling process will enter the gen_server receive loop and become a gen_server process.

各界对hibernate 的使用

进程hibernate 会清空进程的stack 并进行GC ,并且进程被resume 时,需要为进程重新分配heap 资源. hibernate 能够为虚拟机节省更多的内存资源, 但是也会加重CPU的负担. 那社区里对这一特性是怎么使用的?

Mochiweb

在大名鼎鼎的 A Million-user Comet Application with Mochiweb 中, 作者提到了使用hibernate 作为优化手段之一.

This sounds reasonable - let's try hibernating after every message and see what happens.

并且起到了立竿见影的效果:

Judicious use of hibernate means the mochiweb application memory levels out at 78MB Resident with 10k connections, much better than the 450MB we saw in Part 1. There was no significant increase in CPU usage.

是的,Mochiweb 的使用方式, 是在接收到message 之后立即使用hibernate.

Ejabberd

Ejabberd 对hibernate 的使用比较谨慎, 只有在进程未收到任何信息一段时间后, 才使用hibernate .

ejabberd_receiver module 是 Ejabberd 框架中的socket message 接收module, ejabberd_receiver 是gen_server 进程,衔接socket 和C2S gen_fsm 进程. ejabberd_receiver 设置了一个超时时间, 超时时间被触发(也就是进程收到timeout)后, ejabberd_receiver 进程会调用proc_lib:hibernate/3 .而当有新的消息sent 到该进程之后, hibernate 会使用 gen_server:enter_loop/3 函数, 唤醒ejabberd_receiver 进程并重新进入gen_server process .

可以看出 Ejabberd 对 hibernate 的使用,比较谨慎,只有当进程在一段时间内未收到消息(也就是一段时间内空闲),才会使用hibernate .

 handle_info(timeout, State) ->
proc_lib:hibernate(gen_server, enter_loop,
[?MODULE, [], State]),

RabbitMQ

RabbitMQ 同样使用了hibernate, 是在gen_server2 代码中,同样是使用了Ejabberd 类似的方式(进程一段时间空闲后, 才使用hibernate).

使用了 gen_server2 behavior module 的init 函数:

 init(Q) ->
process_flag(trap_exit, true),
?store_proc_name(Q#amqqueue.name),
{ok, init_state(Q#amqqueue{pid = self()}), hibernate,
{backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE},
?MODULE}.

L5 定义了hibernate 相关的参数.

而在gen_server2 module 中, 回调user module init 的方法:

         {ok, State, Timeout, Backoff = {backoff, _, _, _}, Mod1} ->
Backoff1 = extend_backoff(Backoff),
proc_lib:init_ack(Starter, {ok, self()}),
loop(GS2State #gs2_state { mod = Mod1,
state = State,
time = Timeout,
timeout_state = Backoff1 });

会将backoff hibernate 相关参数与gs2_state 一同作为 MAIN loop 的参数.紧接着,gen_server2 loop/1 函数进入 process_next_msg/1 :

 process_next_msg(GS2State = #gs2_state { time          = Time,
timeout_state = TimeoutState,
queue = Queue }) ->
case priority_queue:out(Queue) of
{{value, Msg}, Queue1} ->
process_msg(Msg, GS2State #gs2_state { queue = Queue1 });
{empty, Queue1} ->
{Time1, HibOnTimeout}
= case {Time, TimeoutState} of
{hibernate, {backoff, Current, _Min, _Desired, _RSt}} ->
{Current, true};
{hibernate, _} ->
%% wake_hib/7 will set Time to hibernate. If
%% we were woken and didn't receive a msg
%% then we will get here and need a sensible
%% value for Time1, otherwise we crash.
%% R13B1 always waits infinitely when waking
%% from hibernation, so that's what we do
%% here too.
{infinity, false};
_ -> {Time, false}
end,
receive
Input ->
%% Time could be 'hibernate' here, so *don't* call loop
process_next_msg(
drain(in(Input, GS2State #gs2_state { queue = Queue1 })))
after Time1 ->
case HibOnTimeout of
true ->
pre_hibernate(
GS2State #gs2_state { queue = Queue1 });
false ->
process_msg(timeout,
GS2State #gs2_state { queue = Queue1 })
end
end
end.

在priority_queue 队列为空(L7)的情况下, 等待message(L23) 并设置 ?HIBERNATE_AFTER_MIN (L11, L28)超时, 超时触发之后, 首先回调 user module 的handle_pre_hibernate callback 方法(gen_server2 特有), 最后调用hibernate .

可以看出RabbitMQ 使用hibernate 的方式更为谨慎.

参考文献:

1, http://blog.yufeng.info/archives/1615

2, http://www.erlang.org/doc/man/erlang.html#hibernate-3

3, http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-2

4, Characterizing the Scalability of Erlang VM

Erlang generic standard behaviours -- gen_server hibernate的更多相关文章

  1. Erlang generic standard behaviours -- gen_server system msg

    这是Erlang generic standard behaviors gen_server 分析的系列的最后一篇,主要分析gen_server module 辅助性的功能函数. 在gen_serve ...

  2. Erlang generic standard behaviours -- gen_server module

    在分析完gen module (http://www.cnblogs.com/--00/p/4271386.html)之后,就可以开始进入gen_server 的主体module 了.gen_serv ...

  3. Erlang generic standard behaviours -- gen_server noblock call

    在Erlang 系统中,经常需要gen_server 进程来处理共享性的数据,也就是总希望一个gen_server 进程来为多个普通进程提供某种通用性的服务,这也是gen_server 设计的初衷.但 ...

  4. Erlang generic standard behaviours -- gen_server terminate

    gen_server 主体 module 已经分析完了(http://www.cnblogs.com/--00/p/4271982.html),接着,分析下gen_server 中的terminate ...

  5. Erlang generic standard behaviours -- gen

    在分析 gen_server (或者是gen_fsm )之前,首先应该弄明白,gen 这个module . -module(gen). -compile({inline,[get_node/1]}). ...

  6. Erlang generic standard behaviours -- summary

    gen_server 相关的片段分析得也差不多了, 这篇作为一个简要的总结.这一系列相关的分析暂且告一段落(之后如有必要,还会回来的 ^^ ),下一个系列主要是以pool 相关, 包括但不仅限于开源项 ...

  7. erlang四大behaviour之一gen_server

      来源:http://www.cnblogs.com/puputu/articles/1701017.html erlang程序设计里面有个设计原则就是把你的进程构造成树,把共用代码提出来,特定功能 ...

  8. erlang四大behaviour之一gen_server(转载)

    erlang程序设计里面有个设计原则就是把你的进程构造成树,把共用代码提出来,特定功能用自己的module实现,这也就是behaviour了,应用behaviour可以减少与本身事务无关的代码量,设计 ...

  9. gen_server的模板

    -module(first_gen_server).-behaviour(gen_server).-export([init/1, handle_call/3, handle_cast/2, hand ...

随机推荐

  1. Json CPP 中文支持与入门示例

    在每一个Json Cpp自带*.cpp文件头加上: #include "stdafx.h" 将Json Cpp对自带的头文件的引用修改为单引号方式,例如json_reader.cp ...

  2. jface的CheckboxTreeViewer实现单选

    需求:使用FilteredTree实现一个下面这样的Dialog,要求Check框单选,即只能选择一个:当选择新的时候,旧的不选.说明:FilteredTree自带一个文本输入框. 1.自己的类继承o ...

  3. LeetCode344:Reverse String@Python

    Write a function that takes a string as input and returns the string reversed. Example: Given s = &q ...

  4. IOS百度地图之--->第一篇《环境配置与基本使用》

    Ios 百度地图SDK简易使用说明:http://developer.baidu.com/map/index.php?title=iossdk 先道歉:对于原来上传的Demo我很抱歉,什么都没有,也没 ...

  5. 如何使用 ui-router-extras

    为了使用ui-router创建tabs构架,使用ui-router-extras 使用方法: 0. 安装包 bower install ui-router-extras --save-dev 1. 引 ...

  6. 集成 ssh第一阶段

    1.添加spring支持,包含spring-hibernate和spring-struts2.添加struts支持,包含struts-spring3.添加hibernate支持,在spring配置文件 ...

  7. JSP + JDBC + MySQL 读取数据库内容到网页

    创建数据库表 导入JDCB驱动 mysql.jsp <%@ page language="java" %> <%@ page contentType=" ...

  8. response&period;sendRedirect&lpar;url&rpar;与request&period;getRequestDispatcher&lpar;url&rpar;&period;forward&lpar;request&comma;response&rpar;的区别

    response.sendRedirect(url)跳转到指定的URL地址,产生一个新的request,所以要传递参数只有在url后加参数,如: url?id=1.request.getRequest ...

  9. avcodec&lowbar;decode&lowbar;video2少帧问题

    使用libav转码视频时发现一个问题:使用下面这段代码解码视频时,解码中会不时丢掉几帧. ){ ret = avcodec_decode_video2(video_dec_ctx, vframe, & ...

  10. Python简单实现邮件群发

    Python简单实现邮件群发 import smtplib from email.mime.text import MIMEText from email.utils import formatadd ...