檢視原始碼 peer (stdlib v6.2)

啟動並控制連結的 Erlang 節點。

此模組提供啟動連結的 Erlang 節點的函數。產生新節點的節點稱為origin,而新啟動的節點稱為peer節點或對等節點。當對等節點失去與 origin 的控制連線時,會自動終止。此連線可以是 Erlang 分散式連線,或替代連線 - TCP 或標準 I/O。替代連線提供即使 Erlang 分散式不可用時,也能執行遠端程序呼叫的方式,允許測試分散式本身。

對等節點的終端輸入/輸出會透過 origin 轉發。如果請求標準 I/O 替代連線,主控台輸出也會透過 origin 傳遞,允許偵錯節點啟動和啟動腳本的執行(請參閱 -init_debug)。檔案 I/O 不會被重新導向,這與 slave 的行為相反。

對等節點可以在相同或不同的主機上啟動(透過 ssh)或在單獨的容器中啟動(例如 Docker)。當對等節點在與 origin 相同的主機上啟動時,它會繼承 origin 的目前目錄和環境變數。

注意

此模組旨在促進使用 Common Test 的多節點測試。使用 ?CT_PEER() 巨集,根據 Common Test 的慣例啟動連結的對等節點:將當機轉儲寫入特定位置,節點名稱以模組名稱、呼叫函數和 origin OS 程序 ID 為前綴)。如果您需要更多控制,請使用 random_name/1 來建立足夠獨特的節點名稱。

在沒有替代連線的情況下啟動的對等節點的行為與 slave 類似。當請求替代連線時,其行為類似於 test_server:start_node(Name, peer, Args).

範例

以下範例實作了一個測試套件,啟動額外的 Erlang 節點。它採用多種技術來加速測試並可靠地關閉對等節點

  • 對等節點啟動時連結到測試執行器程序。如果測試案例失敗,對等節點會自動停止,而不會在背景中留下任何異常節點執行
  • 用於啟動對等節點的參數會儲存在控制程序的狀態中,以供手動分析。如果測試案例失敗,當機報告會包含這些參數
  • 多個測試案例可以同時執行,從而加速整體測試過程,即使同時執行同一測試套件的多個實例,對等節點名稱也是唯一的
-module(my_SUITE).
-behaviour(ct_suite).
-export([all/0, groups/0]).
-export([basic/1, args/1, named/1, restart_node/1, multi_node/1]).

-include_lib("common_test/include/ct.hrl").

groups() ->
    [{quick, [parallel],
        [basic, args, named, restart_node, multi_node]}].

all() ->
    [{group, quick}].

basic(Config) when is_list(Config) ->
    {ok, Peer, _Node} = ?CT_PEER(),
    peer:stop(Peer).

args(Config) when is_list(Config) ->
    %% specify additional arguments to the new node
    {ok, Peer, _Node} = ?CT_PEER(["-emu_flavor", "smp"]),
    peer:stop(Peer).

named(Config) when is_list(Config) ->
    %% pass test case name down to function starting nodes
    Peer = start_node_impl(named_test),
    peer:stop(Peer).

start_node_impl(ActualTestCase) ->
    {ok, Peer, Node} = ?CT_PEER(#{name => ?CT_PEER_NAME(ActualTestCase)}),
    %% extra setup needed for multiple test cases
    ok = rpc:call(Node, application, set_env, [kernel, key, value]),
    Peer.

restart_node(Config) when is_list(Config) ->
    Name = ?CT_PEER_NAME(),
    {ok, Peer, Node} = ?CT_PEER(#{name => Name}),
    peer:stop(Peer),
    %% restart the node with the same name as before
    {ok, Peer2, Node} = ?CT_PEER(#{name => Name, args => ["+fnl"]}),
    peer:stop(Peer2).

下一個範例示範如何同時啟動多個節點

multi_node(Config) when is_list(Config) ->
    Peers = [?CT_PEER(#{wait_boot => {self(), tag}})
        || _ <- lists:seq(1, 4)],
    %% wait for all nodes to complete boot process, get their names:
    _Nodes = [receive {tag, {started, Node, Peer}} -> Node end
        || {ok, Peer} <- Peers],
    [peer:stop(Peer) || {ok, Peer} <- Peers].

在不同的主機上啟動對等節點。需要設定基於 ssh 金鑰的身份驗證,允許「another_host」連線,而無需密碼提示。

Ssh = os:find_executable("ssh"),
peer:start_link(#{exec => {Ssh, ["another_host", "erl"]},
    connection => standard_io}),

以下 Common Test 案例示範 Docker 整合,啟動兩個主機名稱為「one」和「two」的容器。在此範例中,在容器內執行的 Erlang 節點會形成一個 Erlang 叢集。

docker(Config) when is_list(Config) ->
    Docker = os:find_executable("docker"),
    PrivDir = proplists:get_value(priv_dir, Config),
    build_release(PrivDir),
    build_image(PrivDir),

    %% start two Docker containers
    {ok, Peer, Node} = peer:start_link(#{name => lambda,
        connection => standard_io,
        exec => {Docker, ["run", "-h", "one", "-i", "lambda"]}}),
    {ok, Peer2, Node2} = peer:start_link(#{name => lambda,
        connection => standard_io,
        exec => {Docker, ["run", "-h", "two", "-i", "lambda"]}}),

    %% find IP address of the second node using alternative connection RPC
    {ok, Ips} = peer:call(Peer2, inet, getifaddrs, []),
    {"eth0", Eth0} = lists:keyfind("eth0", 1, Ips),
    {addr, Ip} = lists:keyfind(addr, 1, Eth0),

    %% make first node to discover second one
    ok = peer:call(Peer, inet_db, set_lookup, [[file]]),
    ok = peer:call(Peer, inet_db, add_host, [Ip, ["two"]]),

    %% join a cluster
    true = peer:call(Peer, net_kernel, connect_node, [Node2]),
    %% verify that second peer node has only the first node visible
    [Node] = peer:call(Peer2, erlang, nodes, []),

    %% stop peers, causing containers to also stop
    peer:stop(Peer2),
    peer:stop(Peer).

build_release(Dir) ->
    %% load sasl.app file, otherwise application:get_key will fail
    application:load(sasl),
    %% create *.rel - release file
    RelFile = filename:join(Dir, "lambda.rel"),
    Release = {release, {"lambda", "1.0.0"},
        {erts, erlang:system_info(version)},
        [{App, begin {ok, Vsn} = application:get_key(App, vsn), Vsn end}
            || App <- [kernel, stdlib, sasl]]},
    ok = file:write_file(RelFile, list_to_binary(lists:flatten(
        io_lib:format("~tp.", [Release])))),
    RelFileNoExt = filename:join(Dir, "lambda"),

    %% create boot script
    {ok, systools_make, []} = systools:make_script(RelFileNoExt,
        [silent, {outdir, Dir}]),
    %% package release into *.tar.gz
    ok = systools:make_tar(RelFileNoExt, [{erts, code:root_dir()}]).

build_image(Dir) ->
    %% Create Dockerfile example, working only for Ubuntu 20.04
    %% Expose port 4445, and make Erlang distribution to listen
    %%  on this port, and connect to it without EPMD
    %% Set cookie on both nodes to be the same.
    BuildScript = filename:join(Dir, "Dockerfile"),
    Dockerfile =
      "FROM ubuntu:20.04 as runner\n"
      "EXPOSE 4445\n"
      "WORKDIR /opt/lambda\n"
      "COPY lambda.tar.gz /tmp\n"
      "RUN tar -zxvf /tmp/lambda.tar.gz -C /opt/lambda\n"
      "ENTRYPOINT [\"/opt/lambda/erts-" ++ erlang:system_info(version) ++
      "/bin/dyn_erl\", \"-boot\", \"/opt/lambda/releases/1.0.0/start\","
      " \"-kernel\", \"inet_dist_listen_min\", \"4445\","
      " \"-erl_epmd_port\", \"4445\","
      " \"-setcookie\", \"secret\"]\n",
    ok = file:write_file(BuildScript, Dockerfile),
    os:cmd("docker build -t lambda " ++ Dir).

摘要

類型

origin 和對等節點之間的替代連線。當連線關閉時,對等節點會自動終止。

斷線逾時。請參閱 stop()

覆寫要用來啟動對等節點的可執行檔。

對等節點狀態。

識別對等節點的控制程序。

透過 start/1start_link/0,1 啟動 peer 節點時可以使用的選項。

以毫秒為單位指定 start/start_link 逾時。可以設定為 false,允許對等節點非同步啟動。如果指定 {Pid, Tag} 而不是逾時,對等節點會將 Tag 傳送至請求的程序。

函數

使用替代連線在對等節點上評估 apply(Module, Function, Args),並傳回對應的值 Result

使用替代連線在對等節點上評估 apply(Module, Function, Args)。不會將任何回應傳遞至呼叫程序。

傳回對等節點狀態。

為目前主機建立一個足夠獨特的節點名稱,結合前綴、唯一數字和目前的 OS 程序 ID。

使用替代連線將 Message 傳送至對等節點上的程序。

使用指定的 start_options/0 啟動對等節點。傳回控制程序和完整的對等節點名稱,除非未請求 wait_boot 且主機名稱事先未知。

以與 start/1 相同的方式啟動對等節點,不同之處在於對等節點會連結到目前執行的程序。如果該程序終止,對等節點也會終止。

停止對等節點。停止節點的方式取決於啟動對等節點時傳遞的 shutdown 選項。目前支援以下 shutdown 選項

類型

連結到此類型

connection()

檢視原始碼 (未匯出) (自 OTP 25.0 起)
-type connection() :: Port :: 0..65535 | {inet:ip_address(), 0..65535} | standard_io.

origin 和對等節點之間的替代連線。當連線關閉時,對等節點會自動終止。

如果 peer_down 啟動旗標設定為 crash,則 origin 節點上的控制程序會以對應的原因結束,有效地提供雙向連結。

connection 設定為埠號時,origin 會開始監聽請求的 TCP 埠,而對等節點會連線至該埠。當其設定為 {IP, Port} 元組時,origin 只會在指定的 IP 上監聽。埠號可以設定為 0 以進行自動選擇。

使用 standard_io 替代連線會啟動附加到 origin 的對等節點(其他連線會使用 -detached 旗標來執行 erl)。在此模式下,對等節點和 origin 會透過 stdin/stdout 通訊。

連結到此類型

disconnect_timeout()

檢視原始碼 (自 OTP 25.0 起)
-type disconnect_timeout() :: 1000..4294967295 | infinity.

斷線逾時。請參閱 stop()

連結到此類型

exec()

檢視原始碼 (自 OTP 25.0 起)
-type exec() :: file:name() | {file:name(), [string()]}.

覆寫要用來啟動對等節點的可執行檔。

預設情況下,它是「erl」的路徑,取自 init:get_argument(progname)。如果 progname 未知,peer 會根據目前的 ERTS 版本做出最佳猜測。

當傳遞元組時,第一個元素是可執行檔的路徑,第二個元素會附加到最終的命令列。這可用於在遠端主機或 Docker 容器中啟動對等節點。請參閱上面的範例。

此選項對於測試與先前版本的向後相容性、安裝在特定路徑中,或當 PATH 中缺少 Erlang 安裝位置時非常有用。

連結到此類型

peer_state()

檢視原始碼 (自 OTP 25.0 起)
-type peer_state() :: booting | running | {down, Reason :: term()}.

對等節點狀態。

連結到此類型

server_ref()

檢視原始碼 (自 OTP 25.0 起)
-type server_ref() :: pid().

識別對等節點的控制程序。

連結到此類型

start_options()

檢視原始碼 (自 OTP 25.0 起)
-type start_options() ::
          #{name => atom() | string(),
            longnames => boolean(),
            host => string(),
            peer_down => stop | continue | crash,
            connection => connection(),
            exec => exec(),
            detached => boolean(),
            args => [string()],
            post_process_args => fun(([string()]) -> [string()]),
            env => [{string(), string()}],
            wait_boot => wait_boot(),
            shutdown => close | halt | {halt, disconnect_timeout()} | disconnect_timeout()}.

透過 start/1start_link/0,1 啟動 peer 節點時可以使用的選項。

  • name - 節點名稱(「@」之前的部分)。當未指定 name,但指定了 host 時,peer 會遵循相容性行為並使用 origin 節點名稱。

  • longnames - 使用長名稱來啟動節點。預設值取自使用 net_kernel:longnames() 的 origin。如果 origin 未分散,則預設值為短名稱。

  • host - 強制使用特定的主機名稱。可用於覆寫預設行為,並啟動「node@localhost」,而不是「node@realhostname」。

  • peer_down - 定義當從對等節點端關閉控制連線時(例如當對等節點當機或傾印核心時)對等控制程序的行為。當設定為 stop(預設值)時,遺失的控制連線會導致控制程序正常結束。將 peer_down 設定為 continue 會使控制程序保持執行,而 crash 會導致控制程序異常結束。

  • connection - 替代連線規格。請參閱 connection 資料類型

  • exec - 用於啟動對等節點的替代機制,例如,使用 ssh 而不是預設的 bash。

  • detached - 定義是否將 -detached 旗標傳遞給啟動的對等節點。無法使用 standard_io 替代連線類型將此選項設定為 false。預設值為 true

  • args - 要附加到 "erl" 命令的額外命令列參數。參數會原封不動地傳遞,不需要或不接受跳脫或加上引號。

  • post_process_args - 允許使用者在啟動對等節點之前,變更傳遞給 exec 的參數。舉例來說,當 exec 程式希望將 "erl" 的參數作為單一參數時,這會很有用。範例:

    peer:start(#{ name => peer:random_name(),
      exec => {os:find_executable("bash"),["-c","erl"]},
      post_process_args =>
         fun(["-c"|Args]) -> ["-c", lists:flatten(lists:join($\s, Args))] end
      }).
  • env - 環境變數及其值的列表。此列表會套用至在本機啟動的可執行檔。如果您需要變更遠端對等節點的環境,請調整 args 以包含 -env ENV_KEY ENV_VALUE

  • wait_boot - 指定啟動/start_link 的逾時時間。請參閱 wait_boot 資料類型

  • shutdown - 指定對等節點停止的行為。請參閱 stop()

連結到此類型

wait_boot()

檢視原始碼 (未匯出) (自 OTP 25.0 起)
-type wait_boot() :: timeout() | {pid(), Tag :: term()} | false.

以毫秒為單位指定 start/start_link 逾時。可以設定為 false,允許對等節點非同步啟動。如果指定 {Pid, Tag} 而不是逾時,對等節點會將 Tag 傳送至請求的程序。

預設值為 15_000 毫秒。

函數

連結至此函數

call(Dest, Module, Function, Args)

檢視原始碼 (自 OTP 25.0 起)
-spec call(Dest :: server_ref(), Module :: module(), Function :: atom(), Args :: [term()]) ->
              Result :: term().

等同於 call(Dest, Module, Function, Args, 5000)

連結至此函數

call(Dest, Module, Function, Args, Timeout)

檢視原始碼 (自 OTP 25.0 起)
-spec call(Dest :: server_ref(),
           Module :: module(),
           Function :: atom(),
           Args :: [term()],
           Timeout :: timeout()) ->
              Result :: term().

使用替代連線在對等節點上評估 apply(Module, Function, Args),並傳回對應的值 Result

Timeout 是一個整數,表示以毫秒為單位的逾時時間,或是原子 infinity,可防止操作永遠逾時。

當未要求替代連線時,此函數會引發帶有 noconnection 原因的 exit 訊號。請使用 erpc 模組透過 Erlang 分散式通訊。

連結至此函數

cast(Dest, Module, Function, Args)

檢視原始碼 (自 OTP 25.0 起)
-spec cast(Dest :: server_ref(), Module :: module(), Function :: atom(), Args :: [term()]) -> ok.

使用替代連線在對等節點上評估 apply(Module, Function, Args)。不會將任何回應傳遞至呼叫程序。

當未設定替代連線時,peer:cast/4 會靜默失敗。請使用 erpc 模組透過 Erlang 分散式通訊。

連結至此函數

get_state(Dest)

檢視原始碼 (自 OTP 25.0 起)
-spec get_state(Dest :: server_ref()) -> peer_state().

傳回對等節點狀態。

初始狀態為 booting;節點會保持在此狀態,直到啟動腳本完成,然後節點會進展到 running。如果節點停止(正常或非正常),狀態會變更為 down

連結至此函數

random_name()

檢視原始碼 (自 OTP 25.0 起)
-spec random_name() -> string().

等同於 random_name(peer)

連結至此函數

random_name(Prefix)

檢視原始碼 (自 OTP 25.0 起)
-spec random_name(Prefix :: string() | atom()) -> string().

為目前主機建立一個足夠獨特的節點名稱,結合前綴、唯一數字和目前的 OS 程序 ID。

注意

為了方便起見,請使用 Common Test 提供的 ?CT_PEER(["erl_arg1"]) 巨集 -include_lib("common_test/include/ct.hrl")。它會使用 Erlang 分散式作為控制通道來啟動新的對等節點,並將呼叫模組的程式碼路徑提供給對等節點,並使用呼叫函數名稱作為名稱前綴。

連結至此函數

send(Dest, To, Message)

檢視原始碼 (自 OTP 25.0 起)
-spec send(Dest :: server_ref(), To :: pid() | atom(), Message :: term()) -> ok.

使用替代連線將 Message 傳送至對等節點上的程序。

如果未設定替代連線,則會靜默失敗。可以使用程序 ID 或註冊名稱來參照程序。

連結至此函數

start(Options)

檢視原始碼 (自 OTP 25.0 起)
-spec start(start_options()) -> {ok, pid()} | {ok, pid(), node()} | {error, Reason}
               when Reason :: term().

使用指定的 start_options/0 啟動對等節點。傳回控制程序和完整的對等節點名稱,除非未請求 wait_boot 且主機名稱事先未知。

連結至此函數

start_link()

檢視原始碼 (自 OTP 25.0 起)
-spec start_link() -> {ok, pid(), node()} | {error, Reason :: term()}.

start_link(#{name => random_name()}) 相同。

連結至此函數

start_link(Options)

檢視原始碼 (自 OTP 25.0 起)
-spec start_link(start_options()) -> {ok, pid()} | {ok, pid(), node()} | {error, Reason}
                    when Reason :: term().

以與 start/1 相同的方式啟動對等節點,不同之處在於對等節點會連結到目前執行的程序。如果該程序終止,對等節點也會終止。

接受 start_options/0。傳回控制程序和完整的對等節點名稱,除非未要求 wait_boot 且主機名稱事先未知。

當要求 standard_io 替代連線,且未將 wait_boot 設定為 false 時,失敗的對等節點啟動序列會導致呼叫端以 {boot_failed, {exit_status, ExitCode}} 原因結束。

連結至此函數

stop(Dest)

檢視原始碼 (自 OTP 25.0 起)
-spec stop(Dest :: server_ref()) -> ok.

停止對等節點。停止節點的方式取決於啟動對等節點時傳遞的 shutdown 選項。目前支援以下 shutdown 選項

  • halt - 這是預設的關閉行為。它的行為如同 shutdown 選項 {halt, DefaultTimeout},其中 DefaultTimeout 目前等於 5000

  • {halt, Timeout :: disconnect_timeout()} - 觸發對等節點上的 erlang:halt() 呼叫,然後等待與對等節點的 Erlang 分散式連線關閉。如果此連線在 Timeout 毫秒後仍未關閉,則會被 peer:stop/1 強制關閉。如需此方面的詳細資訊,請參閱下方的 警告

  • Timeout :: disconnect_timeout() - 觸發對等節點上的 init:stop() 呼叫,然後等待與對等節點的 Erlang 分散式連線關閉。如果此連線在 Timeout 毫秒後仍未關閉,則會被 peer:stop/1 強制關閉。如需此方面的詳細資訊,請參閱下方的 警告

  • close - 關閉與對等節點的控制連線並傳回。這是 peer:stop/1 的呼叫端停止對等節點的最快方式。

    請注意,如果 Erlang 分散式連線未用作控制連線,則在 peer:stop/1 傳回時,可能尚未關閉。另請注意,當 Erlang 分散式連線用作控制連線時,會套用下方的 警告

警告

在 Erlang 分散式連線被 peer:stop/1 關閉的情況下,其他獨立於對等程式碼的程式碼可能會在對等節點停止之前對連線遺失做出反應,這可能會造成不良影響。例如,global 可能會觸發更多與其他節點的 Erlang 分散式連線被關閉。但是,潛在的不良影響不僅限於此。很難說會造成什麼影響,因為這些影響可能由任何與來源節點上的某些物件有連結或監視器的程式碼,或監視與來源節點連線的程式碼所導致。