erlang节点名称注册(erl_epmd)与cookie管理(auth)

net_supkernel中继 dist_ac(distribution applicationcontroller)后启动的监控者(supervisor),除了前文提到的net_kernel(提供节点互联)之外,还有erl_epmd(提供epmd名字注册)auth(提供cookie管理)

拓扑图如下:

            | - erl_epmd
net_sup --- | - auth
            | - net_kernel

本文主要说的是 erl_epmdauth

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_PORTvm参数 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.

  1. 如果vm设置nocookie 参数,直接忽略cookie,否则第二步
  2. 如果vm设置setcookie参数,则保存该 cookie, 否则第三步
  3. $HOME/.erlang.cookie 读取cookie

如上如果要设置一个集群的cookie,有2种方式:

  1. 显示使用setcookie参数(erl -setcookie Cookie),缺点是cookie容易被同一机器上别的用户发现
  2. 使用$HOME/.erlang.cookie文件,保密性强。

3.总结

erl_epmd,auth是节点互联的基础。通过对机制的深入理解,让我们对集群的配置有更多控制权。

4. 参考文献

  1. https://github.com/erlang/otp/blob/master/lib/kernel/src/kernel.erl
  2. https://github.com/erlang/otp/blob/master/lib/kernel/src/erl_distribution.erl
  3. https://github.com/erlang/otp/blob/master/lib/kernel/src/auth.erl
  4. https://github.com/erlang/otp/blob/master/lib/kernel/src/erl_epmd.erl

你可能感兴趣的:(erlang节点名称注册(erl_epmd)与cookie管理(auth))