Distributed Programming
为啥需要分布式的程序?
• 性能,可以把程序的不同部分跑在不同的机器上
• 可靠性,我们可以构建一种容错机制,如果一台机器挂了,可以把他的工作交给其他机器去完成
• 伸缩性,如果向上扩展的话,再好的机器也有个头,但是我们可以向外扩展,添加新的机器进来提高计算能力
这里会介绍两种分布式的实现方式
• 1. 分布式的erlang,提供一种机制让erlang程序运行在一组服务器上,以前介绍的所有的消息机制,spawn等都可以一致的工作
• 分布式的erlang程序应该工作在一个可以信任的环境里,因为任何程序都可以操作任何节点上的东西,一般分布式的erlang都是运行在防火墙后的局域网内
• 2. 基于socket的分布式,使用TCP/IP socket,可以开发更安全的分布式系统,可以工作在不安全的环境下,不过这种模式没有分布式erlang那么强大的功能,但是更安全。
• 实际上,erlang程序工作的基本单元是process,分布式的编程实际上就是在合适的节点上启动合适的进程
• 一般开发分布式的erlang程序的流程
• 首先,开发一个单机程序
• 然后,在同一台机器上测试两个节点的情况
• 最后,在多台物理机器上测试
示例程序:
-module(kvs).
-export([start/0, store/2, lookup/1]).
start() -> register(kvs, spawn(fun() -> loop() end)).
store(Key, Value) -> rpc({store, Key, Value}).
lookup(Key) -> rpc({lookup, Key}).
rpc(Q) ->
kvs ! {self(), Q},
receive
{kvs, Reply} ->
Reply
end.
loop() ->
receive
{From, {store, Key, Value}} ->
put(Key, {ok, Value}),
From ! {kvs, true},
loop();
{From, {lookup, Key}} ->
From ! {kvs, get(Key)},
loop()
end.
• 我们将用这个示例程序来学习分布式的访问
• 1. 在本地访问,很简单
• 2. 在同一台机器的两个节点上访问
• 起两个shell,分别用-sname来给两个节点起名字
• 用rpc:call(Node,Mod,Fun,Params).来访问远程的程序
• 3. 在局域网内的两台机器上分别起节点
• 用-name Name@Domain -setcookie Cookie来启动两个节点,注意Cookie要一致
• 另外就是Domain要能够互相访问到,可以用hosts来绑定
• 访问方法相同
• 4. 在互联网上,两台服务器分别起节点
• 注意在防火墙上要允许4369端口的TCP/UDP访问
• 另外需要指定一个或一系列端口来进行访问,
• erl -name ... -setcookie ... -kernel inet_dist_listen_min Min inet_dist_listen_max Max
• 另外,在多台机器上的程序代码和erlang版本要一致,要保证代码一致,可以:
• 1. 拷贝代码,每个节点分别装载
• 2. 使用共享的文件系统,NFS
• 3. 使用code server,参考erl_prim_loader模块的说明(还没实验过)
• 4. 使用nl(Mod)来广播,可以将已经连接的多个节点同步指定的module
• 只有在发生过远程请求之后,节点才算“已连接”,可以使用net_adm:ping(Node)来连接
分布式erlang的基本要素
• 以节点为基本单元
• 节点要能互相连接需要有同样的cookie,cookie相当于是一个身份验证的机制
• 有几种方式来保证有同样的cookie
• 1. 在启动时指定 erl -setcookie Cookie
• 2. 在$HOME/.erlang.cookie中指定,文件需要400权限
• 3. 在程序里用erlang.set_cookie(Cookie)指定, erlang:set_cookie(node(), C)指定某个node的cookie
• 一些基本的BIF:
• @spec spawn(Node, Fun) -> Pid
• @spec spawn(Node, Mod, Func, ArgList) -> Pid
• @spec spawn_link(Node, Fun) -> Pid
• @spec spawn_link(Node, Mod, Func, ArgList) -> Pid
• @spec disconnect_node(Node) -> bool() | ignored 与Node断开连接
• @spec monitor_node(Node, Flag) -> true 监视Node,如果Flag为true就打开监视,否则关闭监视
• 如果被监视的Node加入或者退出的时候,监视者会收到{nodeup, Node} 或{nodedown,Node}的消息
• @spec node() -> Node 返回本地node的名字,如果没有分布式,返回nonode@nohost
• @spec node(Arg) -> Node
• @spec nodes() -> [Node] 返回已连接的节点名列表
• @spec is_alive() -> bool() 本地节点是否在一个分布式的erlang集群中
• 另外,发送消息{RegName, Node} ! Msg
远程spawn例子
-module(dist_demo).
-export([rpc/4, start/1]).
start(Node) ->
spawn(Node, fun() -> loop() end).
rpc(Pid, M, F, A) ->
Pid ! {rpc, self(), M, F, A},
receive
{Pid, Response} ->
Response
end.
loop() ->
receive
{rpc, Pid, M, F, A} ->
Pid ! {self(), (catch apply(M, F, A))},
loop()
end.
rpc:call(Node, Mod, Function, Args) -> Result | {badrpc, Reason}
socket based distribute.
lib_chan ,可以将某个服务起个别名启动,监听某一端口,并且可以有密码保护,使用这个服务也一样提供密码。erl在服务端和客户端分别起一个进程来做翻译,将erlang的消息和tcp的包进行互相的翻译
-module(mod_name_server).
-export([start_me_up/3]).
start_me_up(MM, _ArgsC, _ArgS) ->
loop(MM).
loop(MM) ->
receive
{chan, MM, {store, K, V}} ->
kvs:store(K, V),
loop(MM);
{chan, MM, {lookup, K}} ->
MM ! {send, kvs:lookup(K)},
loop(MM);
{chan_closed, MM} ->
true
end.
为啥需要分布式的程序?
• 性能,可以把程序的不同部分跑在不同的机器上
• 可靠性,我们可以构建一种容错机制,如果一台机器挂了,可以把他的工作交给其他机器去完成
• 伸缩性,如果向上扩展的话,再好的机器也有个头,但是我们可以向外扩展,添加新的机器进来提高计算能力
这里会介绍两种分布式的实现方式
• 1. 分布式的erlang,提供一种机制让erlang程序运行在一组服务器上,以前介绍的所有的消息机制,spawn等都可以一致的工作
• 分布式的erlang程序应该工作在一个可以信任的环境里,因为任何程序都可以操作任何节点上的东西,一般分布式的erlang都是运行在防火墙后的局域网内
• 2. 基于socket的分布式,使用TCP/IP socket,可以开发更安全的分布式系统,可以工作在不安全的环境下,不过这种模式没有分布式erlang那么强大的功能,但是更安全。
• 实际上,erlang程序工作的基本单元是process,分布式的编程实际上就是在合适的节点上启动合适的进程
• 一般开发分布式的erlang程序的流程
• 首先,开发一个单机程序
• 然后,在同一台机器上测试两个节点的情况
• 最后,在多台物理机器上测试
示例程序:
-module(kvs).
-export([start/0, store/2, lookup/1]).
start() -> register(kvs, spawn(fun() -> loop() end)).
store(Key, Value) -> rpc({store, Key, Value}).
lookup(Key) -> rpc({lookup, Key}).
rpc(Q) ->
kvs ! {self(), Q},
receive
{kvs, Reply} ->
Reply
end.
loop() ->
receive
{From, {store, Key, Value}} ->
put(Key, {ok, Value}),
From ! {kvs, true},
loop();
{From, {lookup, Key}} ->
From ! {kvs, get(Key)},
loop()
end.
• 我们将用这个示例程序来学习分布式的访问
• 1. 在本地访问,很简单
• 2. 在同一台机器的两个节点上访问
• 起两个shell,分别用-sname来给两个节点起名字
• 用rpc:call(Node,Mod,Fun,Params).来访问远程的程序
• 3. 在局域网内的两台机器上分别起节点
• 用-name Name@Domain -setcookie Cookie来启动两个节点,注意Cookie要一致
• 另外就是Domain要能够互相访问到,可以用hosts来绑定
• 访问方法相同
• 4. 在互联网上,两台服务器分别起节点
• 注意在防火墙上要允许4369端口的TCP/UDP访问
• 另外需要指定一个或一系列端口来进行访问,
• erl -name ... -setcookie ... -kernel inet_dist_listen_min Min inet_dist_listen_max Max
• 另外,在多台机器上的程序代码和erlang版本要一致,要保证代码一致,可以:
• 1. 拷贝代码,每个节点分别装载
• 2. 使用共享的文件系统,NFS
• 3. 使用code server,参考erl_prim_loader模块的说明(还没实验过)
• 4. 使用nl(Mod)来广播,可以将已经连接的多个节点同步指定的module
• 只有在发生过远程请求之后,节点才算“已连接”,可以使用net_adm:ping(Node)来连接
分布式erlang的基本要素
• 以节点为基本单元
• 节点要能互相连接需要有同样的cookie,cookie相当于是一个身份验证的机制
• 有几种方式来保证有同样的cookie
• 1. 在启动时指定 erl -setcookie Cookie
• 2. 在$HOME/.erlang.cookie中指定,文件需要400权限
• 3. 在程序里用erlang.set_cookie(Cookie)指定, erlang:set_cookie(node(), C)指定某个node的cookie
• 一些基本的BIF:
• @spec spawn(Node, Fun) -> Pid
• @spec spawn(Node, Mod, Func, ArgList) -> Pid
• @spec spawn_link(Node, Fun) -> Pid
• @spec spawn_link(Node, Mod, Func, ArgList) -> Pid
• @spec disconnect_node(Node) -> bool() | ignored 与Node断开连接
• @spec monitor_node(Node, Flag) -> true 监视Node,如果Flag为true就打开监视,否则关闭监视
• 如果被监视的Node加入或者退出的时候,监视者会收到{nodeup, Node} 或{nodedown,Node}的消息
• @spec node() -> Node 返回本地node的名字,如果没有分布式,返回nonode@nohost
• @spec node(Arg) -> Node
• @spec nodes() -> [Node] 返回已连接的节点名列表
• @spec is_alive() -> bool() 本地节点是否在一个分布式的erlang集群中
• 另外,发送消息{RegName, Node} ! Msg
远程spawn例子
-module(dist_demo).
-export([rpc/4, start/1]).
start(Node) ->
spawn(Node, fun() -> loop() end).
rpc(Pid, M, F, A) ->
Pid ! {rpc, self(), M, F, A},
receive
{Pid, Response} ->
Response
end.
loop() ->
receive
{rpc, Pid, M, F, A} ->
Pid ! {self(), (catch apply(M, F, A))},
loop()
end.
rpc:call(Node, Mod, Function, Args) -> Result | {badrpc, Reason}
socket based distribute.
lib_chan ,可以将某个服务起个别名启动,监听某一端口,并且可以有密码保护,使用这个服务也一样提供密码。erl在服务端和客户端分别起一个进程来做翻译,将erlang的消息和tcp的包进行互相的翻译
-module(mod_name_server).
-export([start_me_up/3]).
start_me_up(MM, _ArgsC, _ArgS) ->
loop(MM).
loop(MM) ->
receive
{chan, MM, {store, K, V}} ->
kvs:store(K, V),
loop(MM);
{chan, MM, {lookup, K}} ->
MM ! {send, kvs:lookup(K)},
loop(MM);
{chan_closed, MM} ->
true
end.