net_sup
是kernel
中继dist_ac(distribution applicationcontroller)
后启动的监控者(supervisor
),除了前文提到的net_kernel(提供节点互联)
之外,还有erl_epmd(提供epmd名字注册)
和auth(提供cookie管理)
。
拓扑图如下:
| - erl_epmd
net_sup --- | - auth
| - net_kernel
本文主要说的是 erl_epmd
和auth
。
1. epmd
(名字注册)
与epmd
相关的配置参数和环境变量:
名字 | 数据类型 | 备注 | |
---|---|---|---|
start_epmd |
true / false | 在开启节点时候是否开启epmd ,默认true |
|
epmd_module |
atom | epmd 模块,默认是erl_epmd |
|
epmd_port |
int | 向指定端口注册Node |
|
ERL_EPMD_ADDRESS |
string | epmd 监听地址 |
|
ERL_EPMD_PORT |
int | epmd 端口号,注意:同一个集群使用同一个端口号,默认4369 |
1.1 如何设置epmd
端口
$ epmd
$ export ERL_EPMD_PORT=Port
$ erlang node
$ erl -epmd_port Port -name ......
% erl_epmd.erl
get_epmd_port() ->
case init:get_argument(epmd_port) of
{ok, [[PortStr | _] | _]} when is_list(PortStr) ->
list_to_integer(PortStr);
error ->
?erlang_daemon_port
end.
环境变量ERL_EPMD_PORT
和vm
参数 epmd_port
配合起来使用,可以在同一个机器上部署多个集群
# 演示了同一台机器部署2个集群
$ 集群一,epmd端口号4000
$ export ERL_EPMD_PORT 4000
$ erl -epmd_port 4000 -name [email protected]
([email protected])1> erl_epmd:names().
{ok,[{"abc",9591}]}
$ 集群二,epmd端口号5000
$ export ERL_EPMD_PORT 5000
$ erl -epmd_port 5000 -name [email protected]
([email protected])1> erl_epmd:names().
{ok,[{"abc",21309}]}
1.2 注册名称过程
% erl_epmd.erl
% 由 net_kernel 发起
handle_call({register, Name, PortNo, Family}, _From, State) ->
case State#state.socket of
P when P < 0 ->
% 只有当没有注册的时候,才允许注册,换句话说,一个Node只会调用一次
case do_register_node(Name, PortNo, Family) of
{alive, Socket, Creation} ->
S = State#state{socket = Socket,port_no = PortNo,name = Name},
{reply, {ok, Creation}, S};
Error ->
{reply, Error, State}
end;
_ ->
{reply, {error, already_registered}, State}
end;
do_register_node(NodeName, TcpPort, Family) ->
Localhost = open(TcpPort) % 连本地的epmd
case Localhost of
{ok, Socket} ->
% Socket 正确连上epmd
Name = to_string(NodeName),
Packet = ...
case gen_tcp:send(Socket, Packet) of
ok ->
% 发送注册包,等待回包
wait_for_reg_reply(Socket, []);
Error ->
close(Socket),
Error
end;
Error ->
Error
end.
wait_for_reg_reply(Socket, SoFar) ->
receive
{tcp, Socket, Data0} ->
...
{tcp_closed, Socket} ->
{error, epmd_close}
after 10000 ->
% 默认10秒超时,并且不能修改。
gen_tcp:close(Socket),
{error, no_reg_reply_from_epmd}
end.
注意点:
- 每个节点只能注册一次
- 注册结束之后保存注册连接
(Socket)
作为后续与epmd
的交互媒介
2. auth
(管理cookie
)
auth
功能比较单一,就是保存本地的cookie
, 相关的vm
参数如下:
名字 | 数据类型 | 备注 |
---|---|---|
nocookie |
- | 主动不设置cookie |
setcookie |
string | 设置节点cookie |
设置cookie
流程
init_cookie() ->
case init:get_argument(nocookie) of
error ->
case init:get_argument(setcookie) of
{ok, [[C0]]} ->
C = list_to_atom(C0),
#state{our_cookie = C, ...};
_ ->
% 从$HOME/.erlang.cookie读取cookie
case read_cookie() of
{error, Error} ->
error_logger:error_msg(Error, []),
%% Is this really this serious?
erlang:error(Error);
{ok, Co} ->
#state{our_cookie = list_to_atom(Co),
other_cookies = ets:new(
cookies,
[?COOKIE_ETS_PROTECTION])}
end
end;
_Other ->
#state{our_cookie = nocookie,other_cookies = ...}
end.
- 如果
vm
设置nocookie
参数,直接忽略cookie
,否则第二步 - 如果
vm
设置setcookie
参数,则保存该cookie
, 否则第三步 - 从
$HOME/.erlang.cookie
读取cookie
如上如果要设置一个集群的cookie
,有2种方式:
- 显示使用
setcookie
参数(erl -setcookie Cookie)
,缺点是cookie
容易被同一机器上别的用户发现 - 使用
$HOME/.erlang.cookie
文件,保密性强。
3.总结
erl_epmd,auth
是节点互联的基础。通过对机制的深入理解,让我们对集群的配置有更多控制权。
4. 参考文献
- https://github.com/erlang/otp/blob/master/lib/kernel/src/kernel.erl
- https://github.com/erlang/otp/blob/master/lib/kernel/src/erl_distribution.erl
- https://github.com/erlang/otp/blob/master/lib/kernel/src/auth.erl
- https://github.com/erlang/otp/blob/master/lib/kernel/src/erl_epmd.erl