当前位置: 首页 > 工具软件 > emqttd > 使用案例 >

emqttd 不是内部或外部命令_emqttd随笔

越飞鸾
2023-12-01

esockd_connection_sup不是一个严格的supervisor。它只是一个gen_server。这是因为他的特殊性决定的。

start_link(Options, MFArgs, Logger) ->

gen_server:start_link(?MODULE, [Options, MFArgs, Logger], []).

supervisor中的必须有重启策略,如果没有在spec中填写默认就是one_for_one。这几种重启策略都会重启子进程。

但是对于sockt连接,断了就是断了,不应该重启的。因此不需要什么重启策略。那么supervisor怎么也得有个监督关系啊,

需要的是当子进程挂了的时候,supervisor要收到消息。

那他是怎么启动子进程(socket连接)的呢?

在esockd_connection_sup.erl中,Conn:start_link(MFArgs)函数调用 emqttd_client:start_link/2 来创建client进程。

esockd_connection_sup.erl:

start_connection(Sup, Mod, Sock, SockFun) ->

case call(Sup, {start_connection, Sock, SockFun}) of

{ok, Pid, Conn} ->

% transfer controlling from acceptor to connection

Mod:controlling_process(Sock, Pid),

Conn:go(Pid),

{ok, Pid};

{error, Error} ->

{error, Error}

end.

handle_call({start_connection, Sock, SockFun}, _From,

State = #state{conn_opts = ConnOpts, mfargs = MFArgs,

curr_clients = Count, access_rules = Rules}) ->

case inet:peername(Sock) of

{ok, {Addr, _Port}} ->

case allowed(Addr, Rules) of

true ->

Conn = esockd_connection:new(Sock, SockFun, ConnOpts),

case catch Conn:start_link(MFArgs) of

{ok, Pid} when is_pid(Pid) ->

...

emqttd_client:start_link/2 调用 proc_lib:spawn_link/3 来启动进程:

emqttd_client.erl:

start_link(Conn, Env) ->

{ok, proc_lib:spawn_link(?MODULE, init, [[Conn, Env]])}.

为什么这里要使用proc_link:spwan_link/3来启动连接进程呢?因为这个函数最终是调用erlang:spawn_link来启动,并自动创建link。

该函数和erlang:start_link的方式区别是spawn_link属于异步启动进程。一调用就会返回子进程ID。

他的用处在emqttd_client:init中看到:

init([Conn0, Env]) ->

{ok, Conn} = Conn0:wait(),

case Conn:peername() of

{ok, Peername} -> do_init(Conn, Env, Peername);

{error, enotconn} -> Conn:fast_close(),

exit(normal);

{error, Reason} -> Conn:fast_close(),

exit({shutdown, Reason})

end.

这里的 Conn0:wait():

esockd_connection.erl:

wait(Conn = ?CONN_MOD) ->

receive {go, Conn} -> upgrade(Conn) end.

使用 receive 来接受消息{go, Conn}。如果emqttd_client:start_link中不使用spawn_link来启动进程,那么在 init 中就会卡死。

这样在 esockd_connection_sup:start_connection(Sup, Mod, Sock, SockFun) 中,Conn:go()就不会被执行。因此出现wait()一直

等待go()发出消息。

如果使用spawn_link就会直接返回,init中执行wait,go被执行后发出消息由wait收到,然后才执行do_init(Conn, Env, Peername)

函数。

另外,esockd_connection_sup 中和子进程link之后,相互都会收到对方 exit 的消息,这样可能 esockd_connection_sup 可能会因为

子进程挂掉而挂掉,为了避免这种情况,esockd_connection_sup 启动的时候在init中设置 process_flag(trap_exit, true), 这样

可以将子进程发送的 exit 消息转化为消息{'EXIT', Pid, Reason},从而避免 esockd_connection_sup 被牵连而挂掉。

handle_info({'EXIT', Pid, Reason}, State = #state{curr_clients = Count, logger = Logger}) ->

...

总结一下:只要理解了spawn_link的异步方式就可以理解wait和go了。

 类似资料: