查看源代码 分布式 Erlang

分布式 Erlang 系统

一个分布式 Erlang 系统由多个互相通信的 Erlang 运行时系统组成。每个这样的运行时系统称为一个节点。当使用 PID 时,不同节点上的进程之间的消息传递,以及链接和监视器都是透明的。然而,注册名称是每个节点本地的。这意味着在使用注册名称发送消息等操作时,还必须指定节点。

分布机制是使用 TCP/IP 套接字实现的。如何在 ERTS 用户指南中描述了如何实现替代载体。

警告

启动分布式节点而未同时指定 -proto_dist inet_tls 将使节点暴露于攻击,这可能会使攻击者完全访问节点,并扩展到集群。当使用不安全的分布式节点时,请确保网络配置为阻止潜在的攻击者。有关如何设置安全的分布式节点的详细信息,请参阅 使用 SSL 进行 Erlang 分布用户指南。

节点

一个节点是一个正在执行的 Erlang 运行时系统,已使用命令行标志 -name(长名称)或 -sname(短名称)指定了名称。

节点名称的格式是一个原子 name@hostname 是用户给定的名称。host 如果使用长名称,则是完整的主机名,如果使用短名称,则是主机名的第一部分。函数 node() 返回节点的名称。

示例

% erl -name dilbert
(dilbert@uab.ericsson.se)1> node().
'[email protected]'

% erl -sname dilbert
(dilbert@uab)1> node().
dilbert@uab

也可以通过调用 net_kernel:start/1 在运行时给出节点名称。

示例

% erl
1> node().
nonode@nohost
2> net_kernel:start([dilbert,shortnames]).
{ok,<0.102.0>}
(dilbert@uab)3> node().
dilbert@uab

注意

具有长节点名称的节点无法与具有短节点名称的节点通信。

节点连接

分布式 Erlang 系统中的节点是松散连接的。第一次使用另一个节点的名称时,例如,如果调用了 spawn(Node, M, F, A)net_adm:ping(Node),则会尝试与该节点建立连接。

默认情况下,连接是可传递的。如果节点 A 连接到节点 B,并且节点 B 连接到节点 C,则节点 A 也会尝试连接到节点 C。可以通过使用命令行标志 -connect_all false 来关闭此功能,请参阅 ERTS 中的 erl

如果某个节点关闭,则会删除与该节点的所有连接。调用 erlang:disconnect_node(Node) 会强制断开与节点的连接。

当前连接的(可见)节点列表由 nodes/0 返回。

epmd

Erlang 端口映射守护进程 epmd 在启动 Erlang 节点的每个主机上自动启动。它负责将符号节点名称映射到机器地址。请参阅 ERTS 中的 epmd

隐藏节点

在分布式 Erlang 系统中,有时需要连接到某个节点,而不连接到所有其他节点。一个例子是用于检查系统状态的某种操作和维护功能,而不会干扰它。为此,可以使用隐藏节点

隐藏节点是使用命令行标志 -hidden 启动的节点。隐藏节点和其他节点之间的连接不是可传递的,必须显式设置。此外,隐藏节点不会显示在 nodes/0 返回的节点列表中。而是必须使用 nodes(hidden)nodes(connected)。这意味着,例如,隐藏节点不会添加到 global 正在跟踪的节点集中。

动态节点名称

如果节点名称设置为 undefined,则节点将以特殊模式启动,作为另一个节点的临时客户端。然后,该节点将从它连接的第一个节点请求动态节点名称。此外,将设置这些分布设置

-dist_listen false -hidden -kernel dist_auto_connect never

由于 -dist_auto_connect 设置为 never,必须调用 net_kernel:connect_node/1 来建立连接。如果第一个建立的连接(为节点提供了其动态名称)关闭,则任何其他连接也将关闭,并且节点将丢失其动态节点名称。可以再次调用 net_kernel:connect_node/1 来获取新的动态节点名称。如果分布被删除然后重新设置,则节点名称可能会更改。

更改

从 Erlang/OTP 23 开始支持动态节点名称功能。临时客户端节点和第一个连接的对等节点(提供动态节点名称)必须至少为 Erlang/OTP 23 才能正常工作。

C 节点

一个 C 节点是一个 C 程序,编写为在分布式 Erlang 系统中充当隐藏节点。库 Erl_Interface 包含用于此目的的函数。有关 C 节点的更多信息,请参阅 Erl_Interface 应用程序和 互操作性教程

安全性

注意

此处的“安全性”意味着加密安全,而是防止意外滥用的安全性,例如防止节点连接到不打算与之通信的集群。

此外,默认情况下,节点之间的通信以明文形式进行。如果您需要强大的安全性,请参阅 SSL 应用程序用户指南中的 使用 TLS 进行 Erlang 分布

此外,以下文本中提到的默认随机 cookie 并非非常不可预测。可以使用 crypto 模块中的原语生成更好的 cookie,但这仍然不能使初始握手加密安全。并且节点间通信仍然是明文的。

身份验证决定允许哪些节点相互通信。在不同的 Erlang 节点网络中,它构建在系统的最低级别。所有节点在连接另一个节点时都使用一个魔术 cookie,它是一个 Erlang 原子。

在连接设置期间,在交换节点名称后,会比较节点相互呈现的魔术 cookie。如果它们不匹配,则连接将被拒绝。cookie 本身永远不会传输,而是使用哈希质询进行比较,尽管不是以加密安全的方式进行的。

在启动时,一个节点被分配一个随机原子作为其默认魔术 cookie,并且假设其他节点的 cookie 为 nocookie。然后,Erlang 网络身份验证服务器 (auth) 的第一个操作是在 用户的主目录中,然后在 filename:basedir(user_config, "erlang") 中搜索名为 .erlang.cookie 的文件。如果这些文件都不存在,则会在用户的主目录中创建 .erlang.cookie 文件。该文件的 UNIX 权限模式设置为八进制 400(用户只读),其内容是一个随机字符串。从文件的内容创建原子 Cookie,并使用 erlang:set_cookie(Cookie) 将本地节点的 cookie 设置为此。这将设置本地节点将用于所有其他节点的默认 cookie。

因此,具有相同 cookie 文件的一组用户会获得可以自由通信的 Erlang 节点,因为它们使用相同的魔术 cookie。想要运行 cookie 文件位于不同文件系统上的节点的用户必须确保他们的 cookie 文件是相同的。

对于使用魔术 cookie Cookie 的节点 Node1,为了能够连接到并接受来自另一个使用不同 cookie DiffCookie 的节点 Node2 的连接,必须首先在 Node1 上调用函数 erlang:set_cookie(Node2, DiffCookie)。具有多个主目录(不同的 cookie 文件)的分布式系统可以通过这种方式处理。

注意

通过此设置,Node1Node2 就使用哪个 cookie 达成一致:Node1Node2 使用其显式配置的 DiffCookie,而 Node2 使用其默认 cookie DiffCookie

您还可以使用 DiffCookie,如果还在建立连接之前在 Node2 中调用 erlang:set_cookie(Node1, DiffCookie),那么 Node1Node2 都不会将其作为默认 cookie。

由于在选择 cookie 之前在连接设置期间交换了节点名称,因此无论哪个节点启动连接,连接设置都可以正常工作。

请注意,当配置 Node1 在与 Node2 通信时使用 Node2 的默认 cookie 时,反之亦然,如果 cookie 不同,则会导致配置损坏,因为两个节点都使用另一个节点(不同的)cookie。

在两个节点之间建立连接时的默认设置是立即连接所有其他可见节点。这样,始终存在一个完全连接的网络。如果存在具有不同 cookie 的节点,则此方法可能不合适(因为为所有可能的节点配置不同的 cookie 可能不可行),并且必须设置命令行标志 -connect_all false,请参阅 ERTS 中的 erl 可执行文件。

可以通过调用 erlang:get_cookie() 来检索本地节点的魔术 cookie。

分布 BIF

以下是一些对分布式编程有用的 BIF(内建函数)

表:分布式 BIF

分布式命令行标志

用于分布式编程的命令行标志示例(有关更多信息,请参阅 ERTS 中的 erl 可执行文件)

命令行标志描述
-connect_all false仅使用显式连接设置。
-hidden使一个节点成为隐藏节点。
-name Name使用长节点名称使运行时系统成为一个节点。
-setcookie Cookie与调用 erlang:set_cookie(Cookie) 相同。
-setcookie Node Cookie与调用 erlang:set_cookie(Node, Cookie) 相同。
-sname Name使用短节点名称使运行时系统成为一个节点。

表:分布式命令行标志

分布式模块

在 Kernel 应用程序中用于分布式编程的模块示例

模块描述
global一个全局名称注册工具。
global_group将节点分组到全局名称注册组。
net_adm各种 Erlang 网络管理例程。
net_kernelErlang 网络内核。

表:用于分布式的内核模块。

在 STDLIB 应用程序中

模块描述
peer启动和控制对等节点。

表:用于分布式的 STDLIB 模块。