1. 使用說明
官方說法:
This server is used to assist diskless Erlang nodes that fetch all Erlang code from another machine.
簡單的說就是幫助那些磁盤較小的機器來從網(wǎng)絡啟動erlang節(jié)點
一般配合erl_prim_loader
使用,boot_server
針對其他的slave
來說斩披,就是本地erl_prim_loader
代理
2. 使用方法
2.1 在master上開啟boot_server服務
erl -setcookie ${COOKIE} -name ${NAME}@${IP} -kernel start_boot_server true -kernel boot_server_slaves = ${SLAVE_HOSTS} .....
注意:
-kernel start_boot_server true 如果使用app.config文件首有,請在app.config文件配置恍风,這個環(huán)境變量是必須的悄谐,否則不會啟動boot_server
SLAVE_HOSTS:其實是可以接入的IP白名單
如果需要動態(tài)添加SLAVE_HOST熟菲,請調(diào)用 erl_boot_server:add_slave/1 方法
example:
erl -setcookie cookie -name boot_server@192.168.36.84 -kernel start_boot_server true -kernel boot_server_slaves = '[{192,168,36,84}]'
2.2 在slave機器上開啟erlang節(jié)點
erl -setcookie ${COOKIE} -name ${NAME}@${IP} -hosts ${HOST} -loader inet ......
注意:
-loader inet 是必須的露久,指定從網(wǎng)絡來載入模塊和文件
-hosts ${HOST} HOST 是 master 節(jié)點的IP, 但是要保證 ${IP} 在2.1中的白名單中
example:
erl -setcookie cookie -name slave@192.168.36.84 -hosts "192.168.36.84" -loader inet
節(jié)點開啟之后就可以正常使用boot_server
上的模塊了
3. 代碼分析
3.1 boot_server
erl_boot_server.erl
init(Slaves) ->
% 4368 UDP端口打開
{ok, U} = gen_udp:open(?EBOOT_PORT, []),
{ok, L} = gen_tcp:listen(0, [binary,{packet,4}]),
{ok, Port} = inet:port(L),
{ok, UPort} = inet:port(U),
Ref = make_ref(),
% Pid 執(zhí)行 boot_loop 函數(shù)
Pid = proc_lib:spawn_link(?MODULE, boot_init, [Ref]),
ok = gen_tcp:controlling_process(L, Pid),
Pid ! {Ref, L},
%% We trap exit inorder to restart boot_init and udp_port
process_flag(trap_exit, true),
{ok, #state{priority = 0,
version = erlang:system_info(version),
udp_sock = U,
udp_port = UPort,
listen_sock = L,
listen_port = Port,
slaves = ordsets:from_list(Slaves),
bootp = Pid}}.
#主要的請求
boot_loop(Socket, PS) ->
receive
{tcp, Socket, Data} ->
PS2 = handle_command(Socket, PS, Data),
boot_loop(Socket, PS2);
{tcp_closed, Socket} ->
true
end.
handle_command(S, PS, Msg) ->
case catch binary_to_term(Msg) of
{get,File} ->
......
{'EXIT',Reason} ->
send_result(S, {error,Reason}),
PS;
_Other ->
send_result(S, {error,unknown_command}),
PS
end.
## 第一次收 erl_priv_loader的請求
handle_info({udp, U, IP, Port, Data}, S0) ->
Token = ?EBOOT_REQUEST ++ S0#state.version,
Valid = member_address(IP, ordsets:to_list(S0#state.slaves)),
% 判斷是否在白名單中(slaves)
case {Valid, Data,Token} of
{true,Token,Token} ->
% 將本地打開的tcp的端口返回給slave,方便下次請求的時候直接通過tcp請求
case gen_udp:send(U,IP,Port,[?EBOOT_REPLY,S0#state.priority,
int16(S0#state.listen_port),
S0#state.version])
.....
{noreply,S0};
{false,_,_} ->
......
{true,_,_} ->
.....
end;
3.2 erl_prim_loader.erl
% erl_prim_loader.erl
% 發(fā)現(xiàn)master 的主要邏輯
find_master(...) -> .....
% 最后得到連上master 的socket 放在state中
find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) ->
case find_loop(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) of
[] ->
find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, Tries, LReSleep);
Servers ->
% [{Priotity,IP,Port}..]
?dbg(servers, Servers),
case connect_master(Servers) of
{ok, Socket} ->
.....
{ok, Socket};
.....
end
end..
send_all(U, [IP | AL], Cmd) ->
?dbg(sendto, {U, IP, ?EBOOT_PORT, Cmd}),
% 向 MasterIP:4368 UDP發(fā)送一個請求
prim_inet:sendto(U, IP, ?EBOOT_PORT, Cmd),
send_all(U, AL, Cmd);
send_all(_U, [], _) -> ok.
% 主循環(huán)更米,處理例如get_file等請求
loop(St0, Parent, Paths) ->
receive
{Pid, {set_path, NewPaths}} when is_pid(Pid) ->
Pid ! {self(), ok},
loop(St0, Parent, to_strs(NewPaths));
{Pid, Req} when is_pid(Pid) ->
case handle_request(Req, Paths, St0) of
ignore ->
ok;
{Resp, #state{} = St1} ->
Pid ! {self(), Resp},
loop(St1, Parent, Paths);
{_, State2, _} ->
exit({bad_state, Req, State2})
end;
......
after St0#state.timeout ->
St1 = handle_timeout(St0, Parent),
loop(St1, Parent, Paths)
end.
% 處理請求的入口
handle_request(Req, Paths, St0) ->
case Req of
{get_path, _} ->
{{ok, Paths}, St0};
{get_file, File} ->
handle_get_file(St0, Paths, File);
.....
_ ->
ignore
end.
對此整個鏈路分析完成
4. 優(yōu)缺點分析
4.1 優(yōu)點
- 可以從遠程加載模塊和文件
- 為了那些硬盤緊缺的機器提供了一種加載方式
- 為運維提供一種
erlang shell
的運維思路
4.2 缺點
-
master
節(jié)點和slave
節(jié)點不能有防火墻,當然這個在內(nèi)網(wǎng)中很容易實現(xiàn)(為什么不通過cookie毫痕?) -
master
,slave
使用的erts
版本要保持一致
5. 總結(jié)
本文簡要的講述了如何從遠程加載模塊文件征峦,分析了實現(xiàn)方式以及優(yōu)缺點。
雖然在生產(chǎn)環(huán)境中消请,我們從來沒有嘗試過栏笆,但是還是推薦一下。