[Erlang]supervisor的child设置为dynamic详解
来源:互联网 发布:教练技术 知乎 编辑:程序博客网 时间:2024/06/10 14:24
原创文章,转载请注明: 转载自系统技术非业余研究
本文链接地址: Erlang supervisor规格的dynamic行为分析
今天benjamin同学在网上问了以下问题:
我在看mochiweb和misultin的代码时有一些不理解的地方,以下是代码:
init({MainSupRef, Port, OptionsTcp, AcceptorsPoolsize, RecvTimeout, SocketMode, CustomOpts}) ->
?LOG_DEBUG(“starting listening ~p socket with options ~p on port ~p”, [SocketMode, OptionsTcp, Port]),
case misultin_socket:listen(Port, OptionsTcp, SocketMode) of
{ok, ListenSocket} ->
Acceptors = [
{{acceptor, N}, {misultin_acceptor, start_link, [MainSupRef, ListenSocket, Port, RecvTimeout, SocketMode, CustomOpts]},
permanent, brutal_kill, worker, dynamic}
|| N ],
{ok, {{one_for_one, 5, 10}, Acceptors}};我不明白的就是为什么是dynamic,我查看supervisor文档,文档上写只有gen_event才应该是dynamic,而这里misultin_acceptor这个进程是使用proc_lib:spawn_link产生的。我在proc_lib的文档中也没有发现这里为什么应该使用dynamic。请您指教。
考虑到这种应用很多,而且基本上supervisor文档上讲的不是很清楚,所以我花时间调查了下,以下是我调查的过程和结果:
我们从 supervisor的 文档开始,可以看到supervisor规格书的定义:
This is the type definition of a child specification:
child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
Id = term()
StartFunc = {M,F,A}
M = F = atom()
A = [term()]
Restart = permanent | transient | temporary
Shutdown = brutal_kill | int()>=0 | infinity
Type = worker | supervisor
Modules = [Module] | dynamic
Module = atom()
其中规格里面module部分可以定义为dynamic的只有gen_event,看文档:
Modules is used by the release handler during code replacement to determine which processes are using a certain module. As a rule of thumb Modules should be a list with one element [Module], where Module is the callback module, if the child process is a supervisor, gen_server or gen_fsm. If the child process is an event manager (gen_event) with a dynamic set of callback modules, Modules should be dynamic. See OTP Design Principles for more information about release handling.
而且进一步翻看Supervisor Behaviour 文档, 里面也写了:
Modules should be a list with one element [Module], where Module is the name of the callback module, if the child process is a supervisor, gen_server or gen_fsm. If the child process is a gen_event, Modules should be dynamic.
那么为什么mochiweb和misultin要定义成dynamic呢?
在正常情况下,被监督的进程如果死掉了,supervisor会根据规格重新启动进程或者做出相应的行动。 但是翻看了supervisor.erl所有的代码也没发现dynamic有什么特殊作用: one_for_one模式下permanent类型的进程出错,supervisor只是负责重新启动,其他的也没做啥。
起先我想不通,但是后来我想到了这个信息可能和release handler的代码热升级有关系。 在热升级的时候,针对supervisor管理的进程,需要知道它是由那个模块apply执行的。 这样才能在模块代码发生变更的时候,好判断出该进程是否要做点升级的准备。 所以规格书不仅仅是给supervisor模块用的,也给release handler模块用。其中的modules部分就是描述那些模块和这个进程有关系。 对于gen_server, gen_fsm behaviour的模块来讲, 它的进程由只有它自己spawn_link来的,所以很好理解填规格的时候,模块部分填他自己就好。 但是gen_event这样的模块,由于一个事件通常会注册好几个模块,而且是动态的,所以规格书就不知道填什么,只好填dynamic. 在热升级需要知道模块的时候,即刻发消息现查询那些模块和这个进程相关。
有了这个理论基础后,我们来看下release_handler_1.erl:510
%%-----------------------------------------------------------------
%% Func: get_supervised_procs/0
%% Purpose: This is the magic function. It finds all process in
%% the system and which modules they execute as a call_back or
%% process module.
%% This is achieved by asking the main supervisor for the
%% applications for all children and their modules
%% (recursively).
%% NOTE: If a supervisor is suspended, it isn't possible to call
%% which_children. Code change on a supervisor should be
%% done in another way; the only code in a supervisor is
%% code for starting children. Therefore, to change a
%% supervisor module, we should load the new version, and then
%% delete the old. Then we should perform the start changes
%% manually, by adding/deleting children.
%% Returns: [{SuperPid, ChildName, ChildPid, Mods}]
%%-----------------------------------------------------------------
%% OTP-3452. For each application the first item contains the pid
%% of the top supervisor, and the name of the supervisor call-back module.
%%-----------------------------------------------------------------
...
get_supervised_procs() ->
...
get_procs(_, _
Sup
) ->
[].
get_dynamic_mods(
Pid
) ->
{ok,
Res
} =
gen:call
(
Pid
, self(), get_modules),
Res
.
我们可以看到 release_handler会给透过gen模块给进程发送get_modules消息来获取相应的模块信息的, 这个消息就是 {From, Tag, get_modules} 。
我们来看下gen_event.erl:277是如何处理这个消息的:
...
{
From
,
Tag
, get_modules} ->
?reply
(get_modules(
MSL
)),
loop(
Parent
,
ServerName
,
MSL
,
Debug
, false);
...
%% Message from the release_handler.
%% The list of modules got to be a set !
get_modules(
MSL
) ->
Mods
= [
Handler
#handler.
module
||
Handler
<-
MSL
],
ordsets:to_list
(
ordsets:from_list
(
Mods
)).
到这里为止,我想大家应该明白了,整个处理流程是如何的,消息是如何流动的。
那我们再回到mochiweb,结合我们的问题来验证下。 先看下 mochiweb_socket_server.erl:294:
% this is what release_handler needs to get a list of modules,
% since our supervisor modules list is set to 'dynamic'
% see sasl-2.1.9.2/src/release_handler_1.erl get_dynamic_mods
handle_info({
From
,
Tag
, get_modules},
State
= #mochiweb_socket_server{name={local,
Mod
}}) ->
From
! {element(2,
Tag
), [
Mod
]},
{noreply,
State
};
% If fo-module(mochiweb_socket_server).
r some reason we can't get the
module
name, send empty list to avoid release_handler timeout:
handle_info({
From
,
Tag
, get_modules},
State
) ->
error_logger:info_msg
(
"mochiweb_socket_server replying to dynamic modules request as '[]'~n"
,[]),
From
! {element(2,
Tag
), []},
{noreply,
State
};
Mods
= [
Handler
#handler.
module
||
Handler
<-
MSL
],
ordsets:to_list
(
ordsets:from_list
(
Mods
)).
?reply
(get_modules(
MSL
)),
loop(
Parent
,
ServerName
,
MSL
,
Debug
, false);
他确实处理了get_modules消息,而且把模块的信息返回去了。
但是诸位会想,为什么要返回这个模块呢? 我们来看下如何使用mochiweb就明白了:
我们看下mochiweb/examples/keepalive/keepalive.erl
-
module
(keepalive).
-
export
([ start/1, loop/1
]).
%% internal export (so hibernate can reach it)
-
export
([ resume/3
]).
-
define
(
LOOP
, {
?MODULE
, loop}).
start(
Options
= [{port, _
Port
}]) ->
mochiweb_http:start
([{name,
?MODULE
}, {loop,
?LOOP
} |
Options
]).
...
他传了{name, ?MODULE}进去,那么最终这个模块keepalive会被 mochiweb_socket_server.erl的get_modules获取的到。 也就是说升级的时候,升级模块要检查keepalive模块是否被变更了。 这就是整个dynamic设计的目的。
流程比较绕,做好一个好的框架确实不容易,要考虑的东西太多。
这里要注意的是misultin就没有处理get_modules消息,所以就会在升级的时候碰到麻烦,不成熟呀!
祝大家玩得开心!
- [Erlang]supervisor的child设置为dynamic详解
- Erlang supervisor规格的dynamic行为分析
- erlang 的 supervisor行为
- erlang的进程树Supervisor
- erlang supervisor
- erlang 监控树 supervisor
- erlang监控树 supervisor
- erlang supervisor学习
- 理解Erlang/OTP Supervisor
- erlang 监控树 supervisor
- Erlang手册supervisor翻译
- erlang 监控树 supervisor
- Erlang 聊天室程序(八) 主题房间---supervisor 的使用
- android 为ExpandableListView中child设置监听器
- [Erlang 0030] 理解Erlang/OTP Supervisor
- 关于 supervisor 的 autorestart 为 unexpected
- 关于 supervisor 的 autorestart 为 unexpected
- erlang OTP supervisor 图解分析
- Android-扫描二维码、生成二维码(Zxing库)
- 动态改变view的style
- spring MVC 2-helloword 入门
- jQuery收藏链接
- SPRING JDBC详细使用
- [Erlang]supervisor的child设置为dynamic详解
- Unity3D:NGUI Srollview子对象中有Button时,点击不能滚动
- Revo Uninstaller Pro v3.1.2 测试和评测:
- Duang一夏,安码企业流程化管控软件优惠季来了! 活动介绍
- hdu 1052 (greedy algorithm)
- OpenLayers学习笔记7——使用javaBean实现用户登录
- 【Linux学习】epoll详解
- Valid Palindrome
- 为Spring集成的Hibernate配置二级缓存