Erlang 分布式編程

2022-07-07 15:41 更新

Erlang分布式編程

下面我們進一步對 ping pong 示例程序進行改進。 這一次,我們要讓 “ping”、“pong” 進程分別位于不同的計算機上。要想讓這個程序工作,你首先的搭建一下分布式的系統(tǒng)環(huán)境。分布式 Erlang 系統(tǒng)的實現(xiàn)提供了基本的安全機制,它阻止未授權(quán)的外部設(shè)備訪問本機的 Erlang 系統(tǒng)。同一個系統(tǒng)中的 Erlang 要想相互通信需要設(shè)置相同的 magic cookie。設(shè)置 magic cookie 最便捷地實現(xiàn)方式就是在你打算運行分布式 Erlang 系統(tǒng)的所有計算機的 home 目錄下創(chuàng)建一個 .erlang.cookie 文件:

  • 在 windows 系統(tǒng)中,home 目錄為環(huán)境變量 $HOME 指定的目錄--這個變量的值可能需要你手動設(shè)置
  • 在 Linux 或者 UNIX 系統(tǒng)中簡單很多,你只需要在執(zhí)行 cd 命令后所進入的目錄下創(chuàng)建一個 .erlang.cookie 文件就可以了。

.erlang.cookie 文件只有一行內(nèi)容,這一行包含一個原子值。例如,在 Linux 或 UNIX 系統(tǒng)的 shell 執(zhí)行如下命令:

$ cd
$ cat > .erlang.cookie
this_is_very_secret
$ chmod 400 .erlang.cookie

使用 chmod 命令讓 .erlang.cookie 文件只有文件擁者可以訪問。這個是必須設(shè)置的。

當你想要啟動 erlang 系統(tǒng)與其它 erlang 系統(tǒng)通信時,你需要給 erlang 系統(tǒng)一個名稱,例如:

$erl -sname my_name

在后面你還會看到更加詳細的內(nèi)容。如果你想嘗試一下分布式 Erlang 系統(tǒng),而又只有一臺計算機,你可以在同一臺計算機上分別啟動兩個 Erlang 系統(tǒng),并分別賦予不同的名稱即可。運行在每個計算機上的 Erlang 被稱為一個 Erang 結(jié)點(Erlang Node)。

(注意:erl -sname 要求所有的結(jié)點在同一個 IP 域內(nèi)。如果我們的 Erlang 結(jié)點位于不同的 IP 域中,則我們需要使用 -name,而且需要指定所有的 IP 地址。)

下面這個修改后的 ping pong 示例程序可以分別運行在兩個結(jié)點之上:

-module(tut17).

-export([start_ping/1, start_pong/0,  ping/2, pong/0]).

ping(0, Pong_Node) ->
    {pong, Pong_Node} ! finished,
    io:format("ping finished~n", []);

ping(N, Pong_Node) ->
    {pong, Pong_Node} ! {ping, self()},
    receive
        pong ->
            io:format("Ping received pong~n", [])
    end,
    ping(N - 1, Pong_Node).

pong() ->
    receive
        finished ->
            io:format("Pong finished~n", []);
        {ping, Ping_PID} ->
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong()
    end.

start_pong() ->
    register(pong, spawn(tut17, pong, [])).

start_ping(Pong_Node) ->
    spawn(tut17, ping, [3, Pong_Node]).

我們假設(shè)這兩臺計算分別稱之為 gollum 與 kosken。在 kosken 上啟動結(jié)點 ping。在 gollum 上啟動結(jié)點 pong。

在 kosken 系統(tǒng)上(Linux/Unix 系統(tǒng)):

kosken> erl -sname ping
Erlang (BEAM) emulator version 5.2.3.7 [hipe] [threads:0]

Eshell V5.2.3.7  (abort with ^G)
(ping@kosken)1>

在 gollum 上:

gollum> erl -sname pong
Erlang (BEAM) emulator version 5.2.3.7 [hipe] [threads:0]

Eshell V5.2.3.7  (abort with ^G)
(pong@gollum)1>

下面,在 gollum 上啟動 "pong" 進程:

(pong@gollum)1> tut17:start_pong().
true

然后在 kosken 上啟動 “ping” 進程(從上面的代碼中可以看出,start_ping 的函數(shù)的其中一個參數(shù)為 “pong” 進程所在結(jié)點的名稱):

(ping@kosken)1> tut17:start_ping(pong@gollum).
<0.37.0>
Ping received pong
Ping received pong 
Ping received pong
ping finished

如上所示,ping pong 程序已經(jīng)開始運行了。在 “pong” 的這一端:

(pong@gollum)2>
Pong received ping                 
Pong received ping                 
Pong received ping                 
Pong finished                      
(pong@gollum)2>

再看一下 tut17 的代碼,你可以看到 pong 函數(shù)根本就沒有發(fā)生任何改變,無論 “ping” 進程運行在哪個結(jié)點下,下面這一行代碼都可以正確的工作:

{ping, Ping_PID} ->
    io:format("Pong received ping~n", []),
    Ping_PID ! pong,

因此,Erlang 的進程標識符中包含了程序運行在哪個結(jié)點上的位置信息。所以,如果你知道了進程的進程標識符,無論進程是運行在本地結(jié)點上還是其它結(jié)點上面,"!" 操作符都可以將消息發(fā)送到該進程。

要想通過進程注冊的名稱向其它結(jié)點上的進程發(fā)送消息,這時候就有一些不同之處了:

{pong, Pong_Node} ! {ping, self()},

這個時候,我們就不能再只用 registered_name 作為參數(shù)了,而需要使用元組 {registered_name,node_name} 作為注冊進程的名稱參數(shù)。

在之前的代碼中了,“ping”、“pong” 進程是在兩個獨立的 Erlang 結(jié)點上通過 shell 啟動的。 spawn 也可以在其它結(jié)點(非本地結(jié)點)啟動新的進程。

下面這段示例代碼也是一個 ping pong 程序,但是這一次 “ping” 是在異地結(jié)點上啟動的:

-module(tut18).

-export([start/1,  ping/2, pong/0]).

ping(0, Pong_Node) ->
    {pong, Pong_Node} ! finished,
    io:format("ping finished~n", []);

ping(N, Pong_Node) ->
    {pong, Pong_Node} ! {ping, self()},
    receive
        pong ->
            io:format("Ping received pong~n", [])
    end,
    ping(N - 1, Pong_Node).

pong() ->
    receive
        finished ->
            io:format("Pong finished~n", []);
        {ping, Ping_PID} ->
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong()
    end.

start(Ping_Node) ->
    register(pong, spawn(tut18, pong, [])),
    spawn(Ping_Node, tut18, ping, [3, node()]).

假設(shè)在 Erlang 系統(tǒng) ping 結(jié)點(注意不是進程 “ping”)已經(jīng)在 kosken 中啟動(譯注:可以理解 Erlang 結(jié)點已經(jīng)啟動),則在 gollum 會有如下的輸出:

<3934.39.0>
Pong received ping
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong
Pong finished
ping finished

注意所有的內(nèi)容都輸出到了 gollum 結(jié)點上。這是因為 I/O 系統(tǒng)發(fā)現(xiàn)進程是由其它結(jié)點啟動的時候,會自將輸出內(nèi)容輸出到啟動進程所在的結(jié)點。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號