[size=x-large]一、进程与消息[/size]
下面的实验用到的测试代码
[size=large]0. 进程及其pid[/size]
启动一个进程
Pid = spawn(dist, echo, [])
通过进程pid,可以直接给这个进程发消息
Pid ! {"hi, myself", self()}.
[size=large]1. 本地注册[/size]
如果在本地节点node1中注册一个进程
可以通过BIF函数registered()察看本节点的所有注册进程。
在本节点内通过注册名给该进程发送消息:
在远程节点node2上给该进程发生消息
[size=large]2. 全局注册[/size]
另外一种方法是对该进程在所有节点组成的集群中进行全局注册
对进程进行全局注册
通过全局注册名发送消息。很遗憾不能通过 ! 给全局注册的远程进程发消息,只能通过global:send/2函数。
[size=large]3. 通过进程pid发送消息[/size]
一般对通过进程pid发送消息都很熟悉。实际上,节点之间也可以通过进程号pid发消息,前提是你要知道远程节点的pid。
前面的例子中可以看到远程进程的pid是<5888.45.0>,当然也可以通过global:whereis_name/1函数查询全局注册进程的pid:
注意到,node1上本地进程pid的格式是<0.45.0>,第一个数字是0,而在远程节点node2上看到的pid就成了<5888,45,0>。当在节点间发送pid数据时,erlang会自动处理这种转化。
通过BIF函数pid/3,也可以由这3个数字计算出一个进程pid,本地的和远程的都可以。
通过BIF函数is_process_alive/1判断一个函数是否还活着。但是这个函数只能判断本地进程,无法知道远程进程的情况。
可以通过BIF函数:erlang:process_info/1察看本地进程的运行信息,但是这个函数不能察看远程进程。
可以通过pid直接给远程节点上的进程发送消息:
[size=large]4. 将函数作为消息发送[/size]
接着玩个小把戏。在FP中,函数和atom、整数、字符串等一样都是数据。这意外着我们可以像发送atom、字符串等一样将本地函数作为消息发送到另外一个节点上,然后执行。
首先机器(ip地址是10.0.0.11)启动节点1,执行如下命令:
然后在erl中定义接收消息的函数:
注意该函数只接收一条消息
创建并登记进程:
在另外一台机器(ip地址是10.0.0.12)上启动节点2:
然后定义作为消息发送出去的函数:
将该函数发送到节点1上:
可以在节点1看到收到F2并执行的结果。
这样任何一个函数都可以发布到任何网络中的一个节点上执行,并不一定非得在定义它的地方执行。
函数作为一种数据可以作为消息发布。对模块而言,erl提供了BIF在各个节点间发布模块,这些BIF在[url=http://www.erlang.org/doc/man/c.html]c模块[/url]中,主要有这两个
[list]
[*]c:nl(Module) 将在所有当前连接的节点中加载模块
[*]c:nc(Module) 编译模块,随后在所有连接的节点中加载模块
[/list]
[size=x-large]关于erlang模块的热更新[/size]
[size=large]一个模块热更新的例子[/size]
运行时更新代码的问题:传统的系统比较难以在运行时更新代码,一般都是停机,替换代码,重新启动完成系统的升级。Erlang号称提供热切换的特性,也就是说在系统不停机时就能更新部分代码,很神奇的样子。热切换机制也与FP函数的这种特点有关,函数与普通对象没有本质的区别,那么既然运行时修改对象不算什么问题,动态更新函数也就是理所当然的了。
由于Erlang变量的不可变性,但是函数可以参数的形式实现动态变化:
loop(Fun) ->
receive
{execute, Arg} ->
Fun(Arg),
loop(Fun);
{update_fun, New_Fun} ->
loop(New_Fun)
end.
当某线程运行此段代码后,该线程就会一直接收别的线程发消息给它,如果收到{execute, Arg}消息,将执行Fun函数,完成相关业务处理;处理完后线程继续等待接收新的消息;
如果要不停机更新代码,就给该进程发送{update_fun, NewFun}消息,NewFun即为要更新的函数。
[size=large]另一个模块热更新的例子[/size]
loop(Fun) ->
receive
{execute, Arg} ->
Fun(Arg),
?MODULE:loop(Fun)
end
当这个模块被重新加载后,再接到一次消息后会自动完成新模块的更新
下面的实验用到的测试代码
-module(dist).
-compile([export_all]).
start() ->
spawn(?MODULE, echo, []).
echo() ->
receive
{M, Pid} -> Pid ! {M, self()} % Pid可以代表本地进程,也可以是远程节点上的进程
end,
echo().
[size=large]0. 进程及其pid[/size]
启动一个进程
Pid = spawn(dist, echo, [])
通过进程pid,可以直接给这个进程发消息
Pid ! {"hi, myself", self()}.
[size=large]1. 本地注册[/size]
如果在本地节点node1中注册一个进程
register(test1,Pid).
可以通过BIF函数registered()察看本节点的所有注册进程。
在本节点内通过注册名给该进程发送消息:
test1 ! Msg.
在远程节点node2上给该进程发生消息
{test1, nodeName@node1} ! {"hi from node1", self()}.
[size=large]2. 全局注册[/size]
另外一种方法是对该进程在所有节点组成的集群中进行全局注册
对进程进行全局注册
$ erl -name node1@10.0.0.11 -setcookie abc
(node1@10.0.0.11)1> nodes().
['node2@10.0.0.22']
(node1@10.0.0.11)2> Pid = dist:start().
<0.45.0>
(node1@10.0.0.11)3> global:register_name(echo_srv, Pid).
(node1@10.0.0.11)4>
(node1@10.0.0.11)5>
通过全局注册名发送消息。很遗憾不能通过 ! 给全局注册的远程进程发消息,只能通过global:send/2函数。
$ erl -name node2@10.0.0.22 -setcookie abc
(node2@10.0.0.22)1> net_kernel:connect('node1@10.0.0.11').
(node2@10.0.0.22)2> global:send(echo_srv, {"hello", self()}).
<5888.45.0>
(node2@10.0.0.22)3> flush().
Shell got {"hello",<5888.45.0>}
[size=large]3. 通过进程pid发送消息[/size]
一般对通过进程pid发送消息都很熟悉。实际上,节点之间也可以通过进程号pid发消息,前提是你要知道远程节点的pid。
前面的例子中可以看到远程进程的pid是<5888.45.0>,当然也可以通过global:whereis_name/1函数查询全局注册进程的pid:
(node2@10.0.0.22)4> RemotePid = global:whereis_name(echo_srv).
<5888.45.0>
注意到,node1上本地进程pid的格式是<0.45.0>,第一个数字是0,而在远程节点node2上看到的pid就成了<5888,45,0>。当在节点间发送pid数据时,erlang会自动处理这种转化。
通过BIF函数pid/3,也可以由这3个数字计算出一个进程pid,本地的和远程的都可以。
(node2@10.0.0.22)4> RemotePid = pid(5888,45,0).
通过BIF函数is_process_alive/1判断一个函数是否还活着。但是这个函数只能判断本地进程,无法知道远程进程的情况。
可以通过BIF函数:erlang:process_info/1察看本地进程的运行信息,但是这个函数不能察看远程进程。
(node2@10.0.0.22)4> erlang:process_info(Pid).
可以通过pid直接给远程节点上的进程发送消息:
(node2@10.0.0.22)4> pid(5888, 45, 0) ! {"hi", self()}.
(node2@10.0.0.22)5> flush().
Shell got {"hi",<0.147.0>}
](node2@10.0.0.22)6> RemotePid ! {"hello", self()}.
(node2@10.0.0.22)7> flush().
Shell got {"hello",<0.147.0>}
[size=large]4. 将函数作为消息发送[/size]
接着玩个小把戏。在FP中,函数和atom、整数、字符串等一样都是数据。这意外着我们可以像发送atom、字符串等一样将本地函数作为消息发送到另外一个节点上,然后执行。
首先机器(ip地址是10.0.0.11)启动节点1,执行如下命令:
erl -name node1@10.0.0.11 -setcookie abc
然后在erl中定义接收消息的函数:
(node1@10.0.0.11)1> F1 = fun()->
(node1@10.0.0.11)1> receive
(node1@10.0.0.11)1> Fun-> io:format("I received !!!~n"), Fun()
(node1@10.0.0.11)1> end
(node1@10.0.0.11)1> end.
注意该函数只接收一条消息
创建并登记进程:
register(test1, spawn(fun() -> F1() end)).
在另外一台机器(ip地址是10.0.0.12)上启动节点2:
erl -name node2@10.0.0.12 -setcookie abc
然后定义作为消息发送出去的函数:
(node2@10.0.0.12)1> F2 = fun() -> io:format("HELLO WORLD~n") end.
将该函数发送到节点1上:
(node2@10.0.0.12)2> {test1, 'node1@10.0.0.11'} ! F2.
可以在节点1看到收到F2并执行的结果。
这样任何一个函数都可以发布到任何网络中的一个节点上执行,并不一定非得在定义它的地方执行。
函数作为一种数据可以作为消息发布。对模块而言,erl提供了BIF在各个节点间发布模块,这些BIF在[url=http://www.erlang.org/doc/man/c.html]c模块[/url]中,主要有这两个
[list]
[*]c:nl(Module) 将在所有当前连接的节点中加载模块
[*]c:nc(Module) 编译模块,随后在所有连接的节点中加载模块
[/list]
[size=x-large]关于erlang模块的热更新[/size]
[size=large]一个模块热更新的例子[/size]
运行时更新代码的问题:传统的系统比较难以在运行时更新代码,一般都是停机,替换代码,重新启动完成系统的升级。Erlang号称提供热切换的特性,也就是说在系统不停机时就能更新部分代码,很神奇的样子。热切换机制也与FP函数的这种特点有关,函数与普通对象没有本质的区别,那么既然运行时修改对象不算什么问题,动态更新函数也就是理所当然的了。
由于Erlang变量的不可变性,但是函数可以参数的形式实现动态变化:
loop(Fun) ->
receive
{execute, Arg} ->
Fun(Arg),
loop(Fun);
{update_fun, New_Fun} ->
loop(New_Fun)
end.
当某线程运行此段代码后,该线程就会一直接收别的线程发消息给它,如果收到{execute, Arg}消息,将执行Fun函数,完成相关业务处理;处理完后线程继续等待接收新的消息;
如果要不停机更新代码,就给该进程发送{update_fun, NewFun}消息,NewFun即为要更新的函数。
[size=large]另一个模块热更新的例子[/size]
loop(Fun) ->
receive
{execute, Arg} ->
Fun(Arg),
?MODULE:loop(Fun)
end
当这个模块被重新加载后,再接到一次消息后会自动完成新模块的更新