查看源码 global (内核 v10.2)

全局名称注册工具。

此模块包含以下服务

  • 全局名称注册
  • 全局锁
  • 维护完全连接的网络

从 OTP 25 开始,global 默认会通过主动断开与报告已丢失与其他节点连接的节点的连接来防止因网络问题导致的重叠分区。这将导致形成完全连接的分区,而不是使网络处于具有重叠分区的状态。

警告

可以使用 prevent_overlapping_partitions 内核参数禁用防止重叠分区的功能,使 global 的行为像以前一样。然而,这对所有期望提供完全连接网络的应用程序来说是有问题的,例如 mnesia,也包括 global 本身。重叠分区的网络可能会导致 global 的内部状态变得不一致。这种不一致即使在这些分区被合并以再次形成完全连接的网络之后仍然存在。对其他期望维持完全连接网络的应用程序的影响可能会有所不同,但它们在分区期间可能会以非常微妙、难以检测的方式出现故障。由于如果没有此修复,您可能会遇到难以检测的问题,因此强烈建议不要禁用此修复。另请注意,此修复必须在网络中的所有节点上启用才能正常工作。

注意

除非同时启用内核参数 connect_allprevent_overlapping_partitions,否则上述任何服务都无法可靠地交付。global API 的调用,即使禁用其中一个或两个,也不会失败。你只会得到不可靠的结果。

这些服务由每个节点上存在的进程 global_name_server 控制。全局名称服务器在节点启动时自动启动。术语全局是指由许多 Erlang 节点组成的系统。

全局注册名称的能力是分布式 Erlang 系统编程中的一个核心概念。在此模块中,提供了 register/2whereis/1 BIF(用于本地名称注册)的等效项,但适用于 Erlang 节点网络。注册的名称是进程标识符(pid)的别名。全局名称服务器监视全局注册的 pid。如果进程终止,则该名称也会全局注销。

注册的名称存储在每个节点上的副本全局名称表中。没有中央存储点。因此,名称到 pid 的转换速度很快,因为它始终在本地完成。对于任何导致全局名称表更改的操作,其他节点上的所有表都会自动更新。

全局锁具有锁标识,并在特定资源上设置。例如,指定的资源可以是 pid。当设置全局锁时,除了锁请求者之外的所有资源都将被拒绝访问锁定的资源。

注册和锁服务都是原子性的。参与这些操作的所有节点都具有相同的信息视图。

全局名称服务器还执行持续监视节点配置更改的关键任务。如果运行全局注册进程的节点发生故障,则该名称将全局注销。为此,全局名称服务器会订阅从模块 net_kernel 发送的 nodeupnodedown 消息。此上下文中相关的内核应用程序变量为 net_setuptimenet_ticktimedist_auto_connect

名称服务器还维护完全连接的网络。例如,如果节点 N1 连接到节点 N2(该节点已连接到 N3),则节点 N1N3 上的全局名称服务器将确保 N1N3 也已连接。在这种情况下,不能使用名称注册服务,但锁定机制仍然有效。

如果全局名称服务器未能连接节点(示例中的 N1N3),则会向错误日志发送警告事件。此类事件的存在并不排除节点稍后连接的可能性(例如,您可以在 Erlang shell 中尝试命令 rpc:call(N1, net_adm, ping, [N2])),但这表示存在网络问题。

注意

如果未正确设置完全连接的网络,请首先尝试增加 net_setuptime 的值。

另请参阅

global_group, net_kernel

概要

类型

用于代表 LockRequesterIdResourceId 上设置或删除锁的锁 id。

函数

同步删除锁 Id

global 已知的所有其他节点断开连接。

该函数注销两个 pid,并将消息 {global_name_conflict, Name, OtherPid} 发送到两个进程。

该函数随机选择其中一个 pid 进行注册,并杀死另一个 pid。

该函数随机选择其中一个 pid 进行注册,并将消息 {global_name_conflict, Name} 发送到另一个 pid。

原子性地更改所有节点上注册的名称 Name,使其引用 Pid

全局将名称 Name 与 pid 相关联,即全局通知 Erlang 节点网络中的所有节点新的全局名称。

返回所有全局注册名称的列表。

将消息 Msg 发送到全局注册为 Name 的 pid。

使用 id/0 在指定节点上设置锁。

将全局名称服务器与此节点已知的所有节点同步。

Id 上设置锁(使用 set_lock/3)。

从 Erlang 节点网络中删除全局注册的名称 Name

返回具有全局注册名称 Name 的 pid。如果该名称未全局注册,则返回 undefined

类型

-type id() :: {ResourceId :: term(), LockRequesterId :: term()}.

用于代表 LockRequesterIdResourceId 上设置或删除锁的锁 id。

链接到此类型

method()

查看源码 (未导出)
-type method() :: fun((Name :: term(), Pid :: pid(), Pid2 :: pid()) -> pid() | none).
链接到此类型

retries()

查看源码 (未导出)
-type retries() :: non_neg_integer() | infinity.
链接到此类型

trans_fun()

查看源码 (未导出)
-type trans_fun() :: function() | {module(), atom()}.

函数

-spec del_lock(Id) -> true when Id :: id().

等效于 del_lock(Id, [node() | nodes()])

-spec del_lock(Id, Nodes) -> true when Id :: id(), Nodes :: [node()].

同步删除锁 Id

链接到此函数

disconnect()

查看源码 (自 OTP 25.1 起)
-spec disconnect() -> [node()].

global 已知的所有其他节点断开连接。

返回一个节点名称列表(顺序未指定),该列表对应于已断开连接的节点。当 global:disconnect/0 返回时,执行的所有断开连接操作均已完成。

断开连接的方式将使仅从 global 节点集群中删除当前节点。如果启用 prevent_overlapping_partitions,并且您通过其他方式从 global 节点集群中的其他节点断开连接,则其他节点上的 global 可能会对剩余的节点进行分区,以确保不会出现重叠分区。即使禁用 prevent_overlapping_partitions,您也最好使用 global:disconnect/0global 节点集群中删除当前节点,因为否则您很可能创建可能导致问题的重叠分区。

请注意,如果要停止节点,无需在停止之前显式调用 global:disconnect/0 将其从 global 节点集群中移除。无论是否启用 prevent_overlapping_partitions,节点停止时都会自动处理集群移除。

如果当前节点已配置为 全局组 的一部分,则只有该组中已连接和/或同步的节点才会被 global 知道,因此 global:disconnect/0断开与这些节点的连接。如果当前节点不是 全局组 的一部分,则所有 已连接可见的节点 都会被 global 知道,因此 global:disconnect/0 将断开与所有这些节点的连接。

请注意,有关已连接节点的信息不会立即到达 global,因此调用者可能会看到 nodes() 返回的结果中包含一个节点,而该节点仍不被 global 知道。但是,当启用 prevent_overlapping_partitions 时,断开连接操作仍然不会导致任何重叠的分区。如果禁用 prevent_overlapping_partitions,则在这种情况下可能会形成重叠的分区。

请注意,当启用 prevent_overlapping_partitions 时,您可能会在其他节点上看到警告报告,表明它们检测到当前节点已断开连接。在这种情况下,这些警告完全无害,可以忽略。

链接到此函数

notify_all_name(Name, Pid1, Pid2)

查看源码
-spec notify_all_name(Name, Pid1, Pid2) -> none when Name :: term(), Pid1 :: pid(), Pid2 :: pid().

该函数注销两个 pid,并将消息 {global_name_conflict, Name, OtherPid} 发送到两个进程。

可以用作 register_name/3re_register_name/3 的名称解析函数。

链接到此函数

random_exit_name(Name, Pid1, Pid2)

查看源码
-spec random_exit_name(Name, Pid1, Pid2) -> pid() when Name :: term(), Pid1 :: pid(), Pid2 :: pid().

该函数随机选择其中一个 pid 进行注册,并杀死另一个 pid。

可以用作 register_name/3re_register_name/3 的名称解析函数。

链接到此函数

random_notify_name(Name, Pid1, Pid2)

查看源码
-spec random_notify_name(Name, Pid1, Pid2) -> pid() when Name :: term(), Pid1 :: pid(), Pid2 :: pid().

该函数随机选择其中一个 pid 进行注册,并将消息 {global_name_conflict, Name} 发送到另一个 pid。

可以用作 register_name/3re_register_name/3 的名称解析函数。

链接到此函数

re_register_name(Name, Pid)

查看源码
-spec re_register_name(Name, Pid) -> yes when Name :: term(), Pid :: pid().

等效于 re_register_name(Name, Pid, fun random_exit_name/3)

链接到此函数

re_register_name(Name, Pid, Resolve)

查看源码
-spec re_register_name(Name, Pid, Resolve) -> yes when Name :: term(), Pid :: pid(), Resolve :: method().

原子性地更改所有节点上注册的名称 Name,使其引用 Pid

函数 Resolve 的行为与 register_name/2,3 中的行为相同。

链接到此函数

register_name(Name, Pid)

查看源码
-spec register_name(Name, Pid) -> yes | no when Name :: term(), Pid :: pid().

等效于 register_name(Name, Pid, fun random_exit_name/3)

链接到此函数

register_name(Name, Pid, Resolve)

查看源码
-spec register_name(Name, Pid, Resolve) -> yes | no
                       when Name :: term(), Pid :: pid(), Resolve :: method().

全局将名称 Name 与 pid 相关联,即全局通知 Erlang 节点网络中的所有节点新的全局名称。

当新节点添加到网络时,它们会被告知已存在的全局注册名称。网络也会被告知新连接节点中的任何全局名称。如果发现任何名称冲突,则会调用函数 Resolve。其目的是确定哪个 pid 是正确的。如果函数崩溃,或者返回除 pid 之外的任何内容,则该名称将被注销。对于每个名称冲突,此函数都会被调用一次。

警告

如果您计划在不重启系统的情况下更改代码,则必须使用外部函数(fun Module:Function/Arity)作为函数 Resolve。 如果使用本地函数,则永远无法替换该函数所属模块的代码。

存在三个预定义的解析函数:random_exit_name/3random_notify_name/3notify_all_name/3

此函数是完全同步的,也就是说,当此函数返回时,该名称要么已在所有节点上注册,要么没有。

如果成功,该函数返回 yes,如果失败,则返回 no。 例如,如果尝试注册已注册的进程或注册一个已在使用的名称的进程,则返回 no

注意

Erlang/OTP R10 及更早版本没有检查进程是否已注册。因此,全局名称表可能会变得不一致。可以通过将 Kernel 应用程序变量 global_multi_name_action 设置为值 allow 来选择旧的(有错误的)行为。

如果具有注册名称的进程死亡,或者节点关闭,则该名称将在所有节点上注销。

-spec registered_names() -> [Name] when Name :: term().

返回所有全局注册名称的列表。

-spec send(Name, Msg) -> Pid when Name :: term(), Msg :: term(), Pid :: pid().

将消息 Msg 发送到全局注册为 Name 的 pid。

如果 Name 不是全局注册的名称,则调用函数将以原因 {badarg, {Name, Msg}} 退出。

-spec set_lock(Id) -> boolean() when Id :: id().

等效于 set_lock(Id, [node() | nodes()], infinity)

-spec set_lock(Id, Nodes) -> boolean() when Id :: id(), Nodes :: [node()].

等效于 set_lock(Id, Nodes, infinity)

链接到此函数

set_lock(Id, Nodes, Retries)

查看源码
-spec set_lock(Id, Nodes, Retries) -> boolean() when Id :: id(), Nodes :: [node()], Retries :: retries().

使用 id/0 在指定节点上设置锁。

如果 ResourceId 上已存在针对 LockRequesterId 以外的其他请求者的锁,并且 Retries 不等于 0,则进程会休眠一段时间,然后稍后尝试执行该操作。 当尝试进行 Retries 次尝试后,将返回 false,否则返回 true。 如果 Retriesinfinity,则最终将返回 true(除非永远不释放锁)。

此函数是完全同步的。

如果持有锁的进程死亡,或者节点关闭,则该进程持有的锁将被删除。

全局名称服务器会跟踪共享同一锁的所有进程,也就是说,如果两个进程设置了同一锁,则这两个进程都必须删除该锁。

此函数不能解决死锁问题。只要进程一次只锁定一个资源,就不会发生死锁。如果某些进程尝试锁定两个或多个资源,则可能发生死锁。由应用程序来检测和纠正死锁。

注意

避免以下 ResourceId 值,否则 Erlang/OTP 将无法正常工作

  • dist_ac
  • global
  • mnesia_adjust_log_writes
  • mnesia_table_lock
-spec sync() -> ok | {error, Reason :: term()}.

将全局名称服务器与此节点已知的所有节点同步。

这些是从 nodes() 返回的节点。当此函数返回时,全局名称服务器会收到来自所有节点的全局信息。当新节点添加到网络时,可以调用此函数。

唯一可能的错误原因 Reason{"global_groups definition error", Error}

-spec trans(Id, Fun) -> Res | aborted when Id :: id(), Fun :: trans_fun(), Res :: term().

等效于 trans(Id, Fun, [node() | nodes()], infinity)

链接到此函数

trans(Id, Fun, Nodes)

查看源码
-spec trans(Id, Fun, Nodes) -> Res | aborted
               when Id :: id(), Fun :: trans_fun(), Nodes :: [node()], Res :: term().

等效于 trans(Id, Fun, Nodes, infinity)

链接到此函数

trans(Id, Fun, Nodes, Retries)

查看源码
-spec trans(Id, Fun, Nodes, Retries) -> Res | aborted
               when
                   Id :: id(),
                   Fun :: trans_fun(),
                   Nodes :: [node()],
                   Retries :: retries(),
                   Res :: term().

Id 上设置锁(使用 set_lock/3)。

如果成功,则会评估 Fun() 并返回结果 Res。 如果锁定尝试失败,则返回 aborted。 如果 Retries 设置为 infinity,则事务不会中止。

infinity 是默认设置,如果未指定 Retries 的值,则使用该设置。

链接到此函数

unregister_name(Name)

查看源码
-spec unregister_name(Name) -> _ when Name :: term().

从 Erlang 节点网络中删除全局注册的名称 Name

-spec whereis_name(Name) -> pid() | undefined when Name :: term().

返回具有全局注册名称 Name 的 pid。如果该名称未全局注册,则返回 undefined