9.7 erlang并发编程杂项-网络内核

net_kernel进程被用于协调分布式Erlang系统。运行时系统会自动向net_kernel发送某些消息。在该进程中执行的代码决定对于不同的系统消息应该采取何种动作。

Erlang系统可在两种模式下运行。它可以作为一个不与其他Erlang系统通讯的封闭系统运行,也可以同其他系统进行通讯,这时我们认为它存活着。通过调用BIF alive/2可以令系统活过来。通常这是由Erlang操作系统而不是用户完成的。以下调用:

erlang:alive(Name, Port)

将通知网络命名服务一个Erlang系统已经启动并可以参与分布式计算了。

Name是一个用于标识该Erlang系统的本地名称。该Erlang系统的外部名称为Name@MachineName,其中MachineName是节点所在的机器名,而字符“@”用于分隔本地名称与机器名。例如,在名为super.eua.ericsson.se的主机上调用erlang:alive(foo,Port)将会启动一个名为foo@super.eua.ericsson.se的Erlang系统,该名称全局唯一。在同一台机器上可以同时运行多个本地名不同的Erlang系统。

Port是一个Erlang端口。外部端口程序必须遵从Erlang分布式系统的内部协议。该程序负责所有的网络操作,如建立与远程节点间的通讯信道以及向这些节点的字节缓冲区读写数据。不同版本的端口程序允许Erlang节点采用不同的网络技术进行通讯。

执行alive/2将使执行该表达式的进程被加入一个可参与分布式计算的Erlang节点池。执行alive/2的进程必须以net_kernel为名进行注册。否则,该BIF调用会失败。要将一个节点从网路中断开,可以关闭分布式端口。

BIF is_alive()可用于检测一个节点是否存活。该BIF返回true或false。

一旦有新节点出现,net_kernel就会收到一条{nodeup, Node}消息;一旦有节点失败,net_kernel也相应会收到一条{nodedown, Node}消息。所有调用spawn/4或spawn_link/4的进程创建请求以及所有采用{Name, Node} ! Message结构向远程注册进程发送消息的请求都会经过net_kernel进程。这使得用户可以通过自定义net_kernel代码来达成多种目的。例如,BIF spawn/4实际上是用Erlang自身实现的。在远程节点创建进程的客户端代码为:

spawn(N,M,F,A) when N /= node() ->
    monitor_node(N, true),
    {net_kernel, N} ! {self(), spawn, M, F, A, group_leader()},
    receive
        {nodedown, N} ->
            R = spawn(erlang, crasher, [N,M,F,A,noconnection]);
        {spawn_reply, Pid} ->
            R = Pid
    end,
    monitor_node(N, false),
    R;
spawn(N,M,F,A) ->
    spawn(M,F,A).

crasher(Node,Mod,Fun,Args,Reason) ->
    exit(Reason).

这段代码的效果是向远程节点上的net_kernel进程发送一条消息。远程的net_kernel负责创建新进程,并告知客户端新进程的Pid。

认证

Erlang系统采用“magic cookies”的方式内建了认证支持。Magic cookie是分配给各个节点的一个保密原子式。每个节点在启动时都会被自动分配一个随机cookie。节点N1要想和节点N2通讯,就必须知道N2的magic cookie。这里不讨论N1如何找出N2的cookie。为了令N1得以和N2通讯,N1必须执行erlang:set_cookie(N2, N2Cookie),其中N2Cookie是N2的cookie值。另外,要令N1能够收到来自N2的响应,N2也必须执行erlang:set_cookie(N1, N1Cookie,其中N1Cookie是N1的cookie值。

Erlang运行时系统会将cookie插入到发送给所有远程节点的所有消息中。若一条消息抵达某节点时携带着错误的cookie,则运行时系统会将这条消息转换为以下格式:

{From,badcookie,To,Message}

其中To是消息接收方的Pid或注册名而From是发送方的Pid。所有未认证的消息发送请求和进程创建请求都会被转为badcookie消息并发送至net_kernel。net_kernel可以任意处置badcookie消息。

以下两个BIF可用于cookie操作:

erlang:get_cookie()

返回自己的magic cookie。

erlang:set_cookie(Node,Cookie)

将节点Node的magic cookie设置为Cookie。获得Node的cookie后可以使用该BIF。它将令后续发送给Node的所有消息都包含Cookie。如果Cookie确实是Node的magic cookie,则消息将直接被发送至Node上的接收进程。如果包含的cookie有误,该消息将在接收端被转为badcookie消息,再被发送至那里的net_kernel。

默认情况下,所有节点都假定所有其他节点的cookie是原子式nocookie,因此初始时所有的远程消息都包含cookie nocookie。

若调用erlang:set_cookie(Node, Cookie)时Node的值为本地节点的名字,则本地节点的magic cookie将被设置为Cookie,同时,其他所有cookie值为nocookie的节点都会变为Cookie。如果所有节点都在启动时执行:

erlang:set_cookie(node(), SecretCookie),

则它们将自动互相认证以便协作。应用如何获取到SecretCookie是一个实现问题。保密cookie应保存于一个仅能由用户读取或仅能由用户组读取的文件中。

在UNIX环境下,节点启动后的默认行为是读取用户HOME目录下名为.erlang.cookie的文件。首先将会对文件的保护权限进行检查,然后便会调用erlang:set_cookie(node(), Cookie),其中Cookie是包含cookie文件内容的原子式。之后,同一用户就可以安全地与其他所有在相同用户ID下运行的Erlang节点进行通讯了(假设所有节点都在同一文件系统下运行)。如果节点驻留在不同的文件系统中,用户只须保证涉及到的文件系统中的cookie文件的内容相同即可。

net_kernel消息

以下是可以发送给net_kernel的消息的列表:

  • {From,registered_send,To,Mess} 向注册进程To的发送消息Mess的请求。

  • {From,spawn,M,F,A,Gleader} 创建新进程的请求。Gleader是请求发起方进程的group leader。

  • {From,spawn_link,M,F,a,Gleader} 创建新进程并向新进程建立链接的请求。

  • {nodeup,Node} 当系统中有新节点接入时,net_kernel就会收到该消息。这种情况既可能是某远程节点来联络我们,也可能是本地节点上的某个进程向该远程节点首次完成了一次远程操作。

  • {nodedown,Node} 当某节点失败或从本地节点无法联络到某远程节点时,net_kernel就会收到该消息。

  • {From,badcookie,To,Mess} 当有未认证请求发送到本节点时,net_kernel就会收到一条可表征该请求性质的消息。例如,某未认证节点发起了一个进程创建请求,net_kernel就会收到消息:

    {From,badcookie, net_kernel, {From,spawn,M,F,A,Gleader}}
    

转载请注明:《9.7 erlang并发编程杂项-网络内核