May 13, 2021 Erlang
Let's make further improvements to the ping pong sample program. T his time, we're going to have the "ping" and "pong" processes on different computers. F or this program to work, you first set up a distributed system environment. T he implementation of a distributed Erlang system provides a basic security mechanism that prevents unauthorized external devices from accessing the Erlang system on the machine. E rlang in the same system needs to set the same magic cookie to communicate with each other. T he easiest way to set up a magic cookie is to create a .erlang.cookie file in the home directory of all the computers you plan to run a distributed Erlang system:
The .erlang.cookie file contains only one line, which contains an atomic value. F or example, in the shell of a Linux or UNIX system, the following command is executed:
$ cd
$ cat > .erlang.cookie
this_is_very_secret
$ chmod 400 .erlang.cookie
Use the chmod command to make the .erlang.cookie file accessible only to file fans. T his must be set.
When you want to start the erlang system to communicate with other erlang systems, you need to give the erlang system a name, for example:
$erl -sname my_name
You'll see more details later. I f you want to try a distributed Erlang system with only one computer, you can start two Erlang systems on the same computer and give them different names. E rlang running on each computer is called an Erang Node.
(Note: erl -sname requires all nodes to be within the same IP domain.) I f our Erlang nodes are in different IP domains, we need to use -name and specify all IP addresses. )
The following modified ping pong sample program can run on top of two nodes:
-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]).
Let's assume that these two calculations are called gollum and kosken, respectively. S tart the node ping on the kosken. S tart the node pong on the gollum.
On the kosken system (Linux/Unix system):
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>
On 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>
Next, start the "pong" process on gollum:
(pong@gollum)1> tut17:start_pong().
true
Then start the "ping" process on the kosken (as can be seen from the code above, one of the parameters of the function of the start_ping is the name of the node at which the "pong" process is located):
(ping@kosken)1> tut17:start_ping(pong@gollum).
<0.37.0>
Ping received pong
Ping received pong
Ping received pong
ping finished
As shown above, the ping pong program is already running. A t this end of the "pong":
(pong@gollum)2>
Pong received ping
Pong received ping
Pong received ping
Pong finished
(pong@gollum)2>
If you look at the tut17 code, you can see that the pong function has not changed at all, and the following line of code works correctly, regardless of which node the "ping" process is running at:
{ping, Ping_PID} ->
io:format("Pong received ping~n", []),
Ping_PID ! pong,
Therefore, Erlang's process identifier contains location information on which node the program runs on. S o, if you know the process identifier for a process, the "!" operator can send messages to the process, regardless of whether the process is running on a local node or on another node.
There are some differences when you want to send messages to processes on other nodes with a process-registered name:
{pong, Pong_Node} ! {ping, self()},
At this point, we can no longer just use registered_name as an argument, but as the name parameter of the registration process, using the registered_name,node_name of the metagroup.
In previous code, the ping and pong processes were started by shells on two separate Erlang nodes. S pawn can also start a new process at other nodes (non-local nodes).
The following example code is also a ping pong program, but this time the "ping" is started on an offsteppable node:
-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()]).
Assuming that the ping node in the Erlang system (note that it is not a process "ping") has been started in kosken (the Erlang node is understandably started), the gollum will have the following output:
<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
Note that everything is output to the gollum node. T his is because when the I/O system discovers that the process is started by another node, it outputs the output from the point at which the start process is located.